Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Attachment Library Laravel Package

van-ons/filament-attachment-library

Filament package for easy attachment uploading and browsing. Install via Composer, run the install command to set up migrations and assets, then extend your custom Filament theme's Tailwind config to render the templates correctly.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimum path from zero to a working attachment field in a Filament panel:

  1. Install the package (pin the major, the current line is v2):
    composer require van-ons/filament-attachment-library:^2.0
    
  2. Run the install command — this is what creates the migrations and publishes the assets:
    php artisan filament-attachment-library:install
    php artisan migrate
    
  3. Create a custom Filament theme. This is not optional: the package's Blade views use Tailwind classes that aren't in Filament's prebuilt CSS.
    php artisan make:filament-theme admin
    
    Then add the package views as a Tailwind source in resources/css/filament/admin/theme.css:
    @source '../../../../vendor/van-ons/filament-attachment-library/resources/**/*.blade.php';
    
    Follow the terminal instructions to register the theme, or if you use Vite:
    ->viteTheme('resources/css/filament/admin/theme.css', 'build')
    
  4. Register the plugin in the panel provider:
    ->plugin(FilamentAttachmentLibrary::make()->navigationGroup('Files'))
    
  5. Pick a disk. Defaults to the public disk; override with ATTACHMENTS_DISK in .env. The README explicitly recommends a disk containing nothing else, to avoid filename collisions with your app's other uploads.

Where to look first: docs/compatibility.md (Filament version matrix — check this before you commit to an upgrade), then the package's two config files config/attachment-library.php and config/glide.php. Most of the package's actual storage behaviour lives one level down in van-ons/laravel-attachment-library, so that repo's docs are the real reference for model-level API.

First real use case: a featured_image on a Resource. Add one line to the form schema, drop the field into the resource, and you have upload + browse-from-library in one control:

use VanOns\FilamentAttachmentLibrary\Forms\Components\AttachmentField;

public static function form(Form $form): Form
{
    return $form->schema([
        AttachmentField::make('featured_image'),
        // ...
    ]);
}

Because the plugin ships its own library page (under the configured navigation group), the fastest way to validate the install is: open that page, upload something, then confirm the file appears on the configured disk and the records appear in the migrations' tables. If the page renders but looks broken, it's a theme problem, not a package problem.

Implementation Patterns

Two distinct storage modes — decide per field, not per project.

Column mode keeps a reference in a column on your own model:

AttachmentField::make('featured_image'),

Relationship mode uses the package's polymorphic attachment relation, requires the trait, and lets one model hold many attachments grouped by collection:

// App\Models\Product
use VanOns\LaravelAttachmentLibrary\Concerns\HasAttachments;

class Product extends Model
{
    use HasAttachments;
}
AttachmentField::make('gallery')->relationship(),
AttachmentField::make('gallery')->relationship()->collection('product_gallery'),

A useful habit picked up from the docs: when the collection name diverges from the field name, add an explicit accessor so the rest of your code doesn't have to know the magic string:

public function gallery(): MorphToMany
{
    return $this->attachmentCollection('gallery');
}

That gives you $product->gallery for eager loading (with('gallery')), API resources, and Blade — which is where most N+1 problems in attachment-heavy admin panels come from.

Treat the field as a picker, not an uploader. The day-to-day win of this package is that editors reuse an existing asset instead of re-uploading it. Design your forms accordingly: a hero_image on twenty pages should point at one library asset, and replacing it in the library should propagate. That means:

  • Keep collection names semantically stable (product_gallery, documents) rather than per-resource.
  • Use the move/replace actions added in v2.0.0 instead of deleting and re-uploading — replace keeps the reference intact.

Front-end rendering goes through the Blade component, never through raw storage paths. Glide handles resizing, and the component accepts either an Attachment instance or an id:

<x-laravel-attachment-library-image :src="$image" />

This matters for focal points (v2.1.0+): if you bypass the component and render Storage::url() yourself, you lose the focal-point crop and any Glide-driven sizing.

Multi-panel apps. Register the plugin per panel and give each panel its own theme (the make:filament-theme [PANEL_NAME] argument exists for exactly this). A public-facing panel and an admin panel will usually want different navigation groups, or no library page at all in one of them.

Form composition tips that hold up in practice:

  • Put AttachmentField in reusable schema partials/relation managers rather than inlining it, so collection names stay consistent across resources.
  • For repeaters with an attachment per item, prefer column mode — relationship mode on a repeated child needs the child model to have the trait.
  • Pair the field with Filament's own ->helperText() explaining collection semantics; editors otherwise treat "gallery" and "product_gallery" as interchangeable.

Testing. Because the field is a Livewire form component, cover it with a Filament form test: fill the state with attachment ids, call the resource's create/save, then assert the record's attachment column or attachments relation contains them. The v0.0.3 changelog entry ("return model after syncing") is a reminder that sync semantics here have had bugs — assert on the persisted relation, not just the returned model.

Gotchas and Tips

Theme integration is the number one failure mode. If the attachment field renders as unstyled markup, suspect the @source glob first: the relative path must be correct from theme.css, and Tailwind's @source needs the path to resolve to the vendor directory as installed (workspaces, path repositories, and vendor symlinks can break it). Check with npx tailwindcss/Vite build output — if the package's classes aren't in the CSS bundle, the path is wrong. Nothing errors; it just looks broken. Also remember to rebuild assets after changing theme.css.

Stale JS assets. The package ships a committed bundle (resources/dist/filament-attachment-library.js). Filament's filament:upgrade composer hook normally republishes it, but projects without that hook must run php artisan filament:assets manually. Release v2.3.7 was literally "built scripts, which was forgotten" — so if the upload/browse UI misbehaves after an update, re-publish assets before filing a bug.

Disk selection deserves a dedicated bucket/directory. ATTACHMENTS_DISK defaults to public; the README's advice to use a disk with no other files is a collision-avoidance recommendation worth heeding. Remember php artisan storage:link for the public disk, and verify Glide can read the disk you chose — image URLs 404 with no PHP exception when the disk isn't web-accessible.

Don't assume the model column is a URL or a path. In column mode the value is an attachment reference consumed by the package. If you need a URL in a table column or API resource, resolve it through the package's view model / the Blade component instead of string-concatenating a storage path. Similarly, don't hand-edit that column in seeders — go through the library's API.

Collection names must match exactly, in two places. ->collection('product_gallery') on the field and attachmentCollection('product_gallery') on the model are separate declarations. A mismatch produces a silently empty relation rather than an error, and it will typically be reported as "the gallery isn't saving."

Upgrades are not trivial. There's a documented v1 → v2 break (see UPGRADING.md) and v2.0.0 was a feature release with move/replace attachment plus Livewire fixes. Read docs/compatibility.md for the Filament matrix before bumping either side; this package tracks Filament majors closely.

Livewire quirks are the historical weak spot. The v1 line is a procession of modal/browser fixes: one shared browser modal to prevent Livewire issues (v1.1.2), multiple smaller Livewire fixes (v1.1.3, v2.0.0-rc5), null currentPath handling (v1.2.2–v1.2.4), wire:key on the lazy Livewire component (v2.3.6). Practical consequences:

  • Keep the package current; most of these are already fixed.
  • Avoid rendering many attachment fields with browser modals on one page if you're on an older minor.
  • If the browser modal shows the wrong directory or throws on a null path, it's a known class of bug — update first.

Focal points only apply to images (v2.1.1: "only show focus point for images"). Don't expect the picker on PDFs, and don't build UI that assumes every attachment has one.

Validation and mime handling. v2.3.6 added "enforce mime for dropped files" and v2.3.4 shipped path/value sanitisation. Both mean: pin a reasonably recent 2.3.x, and if you allow arbitrary file types, test drag-and-drop explicitly rather than only the file dialog — historically the two paths diverged.

Orphaned files are your responsibility. The docs don't promise garbage collection when an attachment record is detached or a model is deleted. Verify the behaviour on the underlying laravel-attachment-library before promising storage hygiene, and consider a scheduled prune for your dedicated attachments disk.

Extension points worth knowing: the two config files (attachment-library.php for storage/collection defaults, glide.php for image sizing and driver) published by the install command; the plugin's navigationGroup() and related panel options; the model-side attachmentCollection() for named relations; and the Blade component for all front-end rendering. Beyond those, customising the browser UI means editing vendor Blade views — there's no documented view-publishing step in the README, so check php artisan vendor:publish listings before assuming one exists.

Maturity caveat. With ~11 stars and a single-digit dependent count, treat this as a young package from an agency (Van Ons) rather than a battle-tested one. The upside is an active changelog and a real install command; the downside is that you will occasionally be the person discovering an edge case. Budget a short spike to validate your specific combination of Filament version, disk driver (S3 vs local), and theme setup before rolling it across many resources.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin