sandeep816/smart-media-library
A Laravel media library for uploading, storing, and managing images and files through an Eloquent-friendly API. Attach media to models, generate thumbnails and conversions, and serve optimized assets from local or cloud storage.
Install the package
composer require sandeep816/smart-media-library
Check the compatibility matrix before upgrading an existing app: this release targets PHP 8.3–8.5, Laravel 12/13, and Filament 5.8. Older Laravel or Filament majors are not covered by the verified matrix.
Publish and migrate
php artisan vendor:publish --tag="smart-media-library-config"
php artisan vendor:publish --tag="smart-media-library-migrations"
php artisan migrate
Also run php artisan storage:link if you intend to serve from the public disk.
Register the Filament plugin in your panel provider (->plugins([SmartMediaLibraryPlugin::make()])) so the central Media Library grid/table pages and the MediaPicker assets become available.
First place to look: the published config file. It controls the default disk, public/private behaviour, conversion definitions, and the authorization hooks. Most day-to-day surprises (wrong URLs, uploads rejected, conversions missing) trace back to this file.
First use case: add the package trait/interface to a model (e.g. Post), then drop a MediaPicker into a Filament resource form:
MediaPicker::make('cover')->single()
Upload once into the central library, reuse the same asset across resources, and let the polymorphic attachment table record the relationship rather than storing raw file paths on your models.
Attaching media to models. Treat the package as the source of truth for files and keep your own columns for metadata only. Prefer the provided relationship/trait methods over writing to the attachment table directly, so ordering, soft deletes, and conversions stay consistent.
Single vs. multiple selection. Use ->single() for hero images, avatars, and PDFs; use ->multiple()->reorderable() for galleries. Reorderable selections persist an order column — read media through the relationship rather than ->get() on the pivot so the ordering is applied for you.
Public vs. private disks. Model this as a per-collection or per-field decision, not a global one. Public assets can be rendered with a direct storage URL; private assets should be resolved through the package's URL/serving mechanism (signed or guarded route) so authorization hooks actually run. Don't hand-build Storage::url() calls for private disks.
Conversions. Define derivative sizes/format once and let the pipeline generate them on upload. In views, always request a named conversion with a fallback to the original rather than assuming the derivative exists — older records imported before a conversion was added will not have it until regeneration runs.
Regeneration workflow. After changing conversion definitions, run the regeneration Artisan command. On any non-trivial library, run it queued and in chunks; it re-encodes every matching asset and will otherwise time out or spike memory. Treat it as a deploy step, not a request-time operation.
Authorization. Wire the package's authorization hooks to your existing policies/gates rather than building a parallel permission system. This is the extension point that decides who can browse the library, upload, and delete — especially important when the library is shared across panels or tenants.
Soft delete and restore. Deleting media is recoverable, but the underlying file usually isn't re-created if it was physically removed. Decide explicitly whether your delete path is soft delete (recoverable record) or force delete (file cleanup), and make that choice visible in the UI so editors don't assume "delete" means "gone forever."
Testing. Use Storage::fake() in your own feature tests and assert on attachment records rather than filesystem contents. The package ships its own suite, so failures after an upgrade are more likely to be your integration (disk config, morph map, Filament version) than the package internals.
Relation::enforceMorphMap(), register your media-attachable models before importing data, or existing attachments will resolve to the wrong class.Storage::url(). The most common bug report pattern: private files render fine in the admin but 403/404 on the front end. Use the package's resolution helpers and verify the route is reachable outside the panel middleware.--queue (or wrap it) in production, and log the failures — a single unreadable source file can abort a batch.vendor:publish --force is safe.How can I help you explore Laravel packages today?