filament/spatie-laravel-media-library-plugin
Filament Spatie Media Library Plugin integrates Spatie's media library with Filament forms, letting you upload and attach files to Eloquent models using collections, custom disks, and file upload options.
Architecture fit. This is a thin adapter, not a platform. It ships no persistence, no storage layer, and no domain model of its own — it maps Filament's component contracts (SpatieMediaLibraryFileUpload, SpatieMediaLibraryImageColumn, SpatieMediaLibraryImageEntry, SpatieMediaLibraryFileAttachmentProvider) onto spatie/laravel-medialibrary's polymorphic media table and HasMedia model contract. Architecturally that is the right shape: media lifecycle (upload, conversions, responsive images, custom properties, disk routing) stays owned by Spatie, while presentation stays owned by Filament. The consequence for a TPM is that adopting this package is really a joint commitment to two upstreams, and its value is entirely derivative — it is worth nothing outside a Filament panel, which is consistent with the low standalone opportunity score.
Integration feasibility. Low code effort, non-trivial configuration effort. The install is three mechanical steps (composer require ...:"^5.0" -W, publish Spatie's migration, php artisan migrate) plus implementing HasMedia/InteractsWithMedia on each model and registering collections. Swapping a FileUpload for SpatieMediaLibraryFileUpload is a one-line change and all base file-upload options carry over. Realistic estimate: 2–5 developer-days for a pilot slice including disk configuration and one backfill script; 1–2 sprints to roll out across a resource-heavy admin with conversions, responsive images, and rich-editor attachments.
Technical risk. Four items belong on the risk register:
directory() and visibility() are ignored and that Spatie does not support private uploads natively; the documented workaround is S3 bucket policy plus visibility('private') so Filament issues temporary URLs. Any workload touching PII, contracts, or health data should be treated as a custom engineering task, not a package feature.directory() and visibility() exist on the parent component and do nothing here. Teams porting existing upload fields will assume their storage layout carried over. Storage-path surprises are the most likely source of a mid-project incident.FILESYSTEM_DISK (and its published config) wins over Spatie's disk config unless a collection explicitly declares a disk. Two teams reading two different docs will reach two different conclusions.Version coupling is the standing risk: the ^5.0 constraint tracks Filament majors, so this package inherits Filament's upgrade cadence plus Spatie's. Metadata quality is also poor — a 32k-star parent repo reporting zero dependents and a release date in the future means registry signals here should not be used for a build-vs-buy decision; verify against Packagist and the monorepo changelog. MIT licensing removes legal friction.
Key questions.
Stack fit. Adopt only if the application is already on Filament 5 + Livewire + Laravel with Spatie Media Library either present or planned. The package slots in as a presentation layer, so it fits cleanly inside panels and resource classes without touching domain services — media access remains $model->getMedia('collection'), which keeps business logic testable outside the UI. Do not adopt it as a reason to introduce Spatie Media Library if the product only needs a handful of avatar uploads; plain FileUpload is cheaper.
Migration path. Sequence to keep blast radius small:
| Phase | Work | Exit criteria |
|---|---|---|
| 0. Spike | Disposable branch: install, migrate, wire one model, one form field | Upload persists, media row created, conversion renders |
| 1. Pilot | One low-risk public collection (avatars or marketing imagery) on one resource | Disk/directory behavior verified in staging, not assumed |
| 2. Backfill | Script existing path-based files via addMediaFromDisk()->toMediaCollection(), keep legacy columns readable |
Row counts and checksums reconcile; legacy columns read-only |
| 3. Expand | Table columns, infolist entries, conversion()/conversionsDisk() per context |
Thumbnails render from conversions, not originals |
| 4. Rich editor | Register rich content attribute, dedicated collection, nullable column | No media cleared on save during a full edit-save-edit cycle |
| 5. Cutover | Drop legacy columns, remove old upload components | — |
Compatibility. Verify before Phase 1: PHP and Laravel versions against Filament 5's matrix, the supported spatie/laravel-medialibrary major, image driver availability (GD vs Imagick) for conversions, and whether the storage driver is local, S3, or S3-compatible. Confirm queue driver and worker concurrency, since conversions are asynchronous.
Sequencing. Land the storage/disk decision before the first upload, because it is the one choice that is expensive to reverse once files exist. Land the rich-editor integration last — it is the only surface with destructive semantics and deserves its own test pass. Keep the Composer constraint pinned to an exact minor in composer.lock and treat -W as an explicit, reviewed action rather than a habit.
Maintenance. Budget a recurring cost tied to Filament major releases. Because the plugin lives inside the Filament monorepo and shares its test suite, it will generally keep pace — but that pace is set by Filament, not by us. Practical policy: one owner, an upgrade checklist entry for every Filament major, and a staging environment with a working queue so conversion regressions surface before production.
Support. Self-serve only. Documentation is split three ways — this README, the Filament file-upload/image component docs, and Spatie's media library docs — and the gaps sit precisely at the seams (which config wins, what is ignored, what gets deleted). Expect engineers to burn time reconciling three sources. There is no vendor SLA; MIT plus a 32k-star monorepo means community response, which is adequate for a glue package and adequate for the risk profile only if we accept that we are the final line of support.
Scaling. Two cost curves. First, storage: responsive images and multiple conversions multiply objects per upload, so model the bill as originals × derivative count, not originals alone. Second, queue throughput: conversions are background work, and upload bursts will queue behind whatever else the worker pool handles. The customHeaders() option (CacheControl) is the cheap lever for CDN efficiency and should be set deliberately at rollout rather than retrofitted.
Failure modes to monitor.
Ramp-up. Low for developers already fluent in Filament's fluent component API — a day to be productive, largely spent reading the three doc sources. The real onboarding investment is the storage and security model (private buckets, temporary URLs, collection design), which is a half-day of deliberate design work that prevents most of the failure modes above. Pair that with a small test convention — Pest/Livewire coverage for form submission and media persistence, Pest Browser for reordering and rich-editor attachment cleanup — and the package becomes a low-drama dependency rather than a recurring surprise.
How can I help you explore Laravel packages today?