Media Browser by Ahmed Elsayed
CommunityA shared media library for Filament 3, 4, and 5. Editors upload and manage images and documents in the panel, then attach those files to records from a form field. One file can be used by many records. Detaching it removes the usage and leaves the file in the library.
Author:
Ahmed Elsayed
Package health
Automated checks of this plugin's Composer package
15 checks
- Skipped: GitHub Actions pinned to SHA
- Skipped: GitLab CI includes pinned to SHA
- Passed: Open security advisories
- Passed: Dependabot PR responsiveness — No open Dependabot PRs.
- Skipped: Renovate MR responsiveness
- Skipped: Dependabot or Renovate configured
- Skipped: Dependency update cooldown configured
- Passed: Provides a security policy
- Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist, github).
- Passed: Commit and release recency — Active: last commit 0 days ago; last release 0 days ago.
-
Passed:
composer.lock not committed by library
—
composer.lockis absent from the released dist archive. - Passed: Dist archive is lean
-
Passed:
Current Laravel version supported
—
Package dependencies resolve together with current Laravel
13.0. -
Passed:
Current PHP version supported
—
Constraint
^8.4supports current PHP8.5. - Skipped: Current Symfony version supported
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Documentation
- Contents
- Requirements
- Installation
- Form field
- Reading and updating attachments
- File URLs
- Configuration
- License
- Contributing
A shared media library for Filament 3, 4, and 5. Editors upload and manage images and documents in the panel, then attach those files to records from a form field. One file can be used by many records. Detaching it removes the usage and leaves the file in the library.
The package ships with two drivers:
| Driver | config('media-browser.driver') |
Storage |
|---|---|---|
| Default | default |
media_browser_files, media_browser_attachments |
| Spatie | spatie |
Spatie media owned by one media_pool row, collection library. Usages are stored in media_attachments. |
Use the default driver for a new library. Use the Spatie driver when the application already stores files with spatie/laravel-medialibrary.
#Contents
- Requirements
- Installation
- Form field
- Reading and updating attachments
- File URLs
- Configuration
- License
- Contributing
#Requirements
- PHP 8.4+
- Laravel 11 or 12
- Filament 3.2, 4, or 5
The Spatie driver also requires spatie/laravel-medialibrary 11+.
#Installation
Install the latest stable release from Packagist:
composer require ahmedelsied/filament-media-browser
php artisan media-browser:install
php artisan migrate
Composer installs the highest stable version, currently v1.0.0. Laravel discovers AhmedElsied\FilamentMediaBrowser\MediaBrowserServiceProvider automatically.
media-browser:install asks whether the project already uses Filament Spatie Media.
| Answer | Driver | Migrations |
|---|---|---|
| No | default |
media_browser_files, media_browser_attachments |
| Yes | spatie |
media_pool, media_attachments |
Answer yes only when spatie/laravel-medialibrary 11+ is installed. The command publishes config/media-browser.php and that driver's migrations. An existing config file keeps its other values, and the driver key is updated. A migration that is already published is left unchanged. The command does not edit .env.
Register the plugin on the panel:
use AhmedElsied\FilamentMediaBrowser\MediaBrowserPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
MediaBrowserPlugin::make(),
]);
}
This adds two resources:
| Resource | URL |
|---|---|
| Image Browser | /media-browser |
| Document Browser | /document-browser |
A signed-in user who can access the panel can upload, inspect, and permanently delete library files. The view modal lists every record that uses the file and links to that record's edit page, or its view page when the resource has no edit page.
To send those links to a parent record, or to set the type label:
MediaBrowserPlugin::make()
->linkTargetUsing(fn (Model $record): Model => $record)
->recordUrlUsing(fn (Model $record): ?string => null)
->typeLabelUsing(fn (Model $record): ?string => null);
linkTargetUsing selects the model that owns the page. recordUrlUsing returns its URL. An empty result falls back to the model's Filament resource. typeLabelUsing replaces the class basename shown before the record title.
Changing driver later requires the other driver's migration. Files are not copied from one driver to the other.
#Form field
MediaBrowserField opens the library. The editor selects an existing file or uploads a new one. Saving the form writes the attachment for that collection. The field does not add a column to the record.
use AhmedElsied\FilamentMediaBrowser\Forms\Components\MediaBrowserField;
MediaBrowserField::make('cover')
->image()
->collection('cover');
MediaBrowserField::make('attachments')
->document()
->multiple()
->collection('attachments');
| Method | Behavior |
|---|---|
image() |
Default. One JPEG, PNG, WebP, or SVG, up to 2048 KB. |
document() |
PDF, DOC, DOCX, PPT, PPTX, TXT, or CSV, up to 5120 KB. |
multiple() |
More than one file, stored in the chosen order. |
collection() |
Collection name used when reading the files back. Defaults to the field name. |
disk() |
Disk for a new upload. |
Any Eloquent model can use the field on the default driver. On the Spatie driver the model must use InteractsWithPoolMedia, as shown below. View pages keep the preview and hide the choose and remove actions.
#Reading and updating attachments
A record stores a link to a library file, grouped by collection name (cover, gallery, and so on). Use the code below from an API resource, a job, or anywhere outside the form.
MediaLibrary::resolve() returns the configured driver.
#Default driver
No trait is required.
use AhmedElsied\FilamentMediaBrowser\MediaLibrary;
$library = MediaLibrary::resolve();
$cover = $library->attachments($post, 'cover');
attachments() returns the MediaBrowserFile models for that collection, in saved order.
To add a file to the library and attach it, pass getKey() from each file model. That value is media_browser_files.id. syncAttachments() replaces one collection. Other collections on the same record are left unchanged, and the library files are not deleted.
$uploaded = $library->storeUpload($upload, 'Homepage hero');
$currentCover = $library->attachments($post, 'cover')->first();
$library->syncAttachments($post, 'gallery', [
$uploaded->getKey(),
$currentCover->getKey(),
]);
$upload is an Illuminate\Http\UploadedFile. Clear a collection by passing an empty list:
$library->syncAttachments($post, 'gallery', []);
#Spatie driver
Implement Spatie's HasMedia and use the pool trait:
use AhmedElsied\FilamentMediaBrowser\Contracts\AttachesMedia;
use AhmedElsied\FilamentMediaBrowser\Traits\InteractsWithPoolMedia;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
class Post extends Model implements AttachesMedia, HasMedia
{
use InteractsWithPoolMedia;
}
getFirstMedia() returns the first file in a collection. getMedia() returns every file in that collection, in order. When a collection has no library usage, both methods fall back to media the model owns directly, including files uploaded with the Filament Spatie field. clearMediaCollection() deletes only those owned files.
$cover = $post->getFirstMedia('cover');
$gallery = $post->getMedia('gallery');
syncAttachedMedia() takes Spatie media.id values from getKey(). The uuid shown in the picker is a different value. When you have a uuid, load the media model first.
use AhmedElsied\FilamentMediaBrowser\MediaLibrary;
$library = MediaLibrary::resolve();
$uploaded = $library->storeUpload($upload, 'Homepage hero');
$picked = $library->findFiles([$uuid])->first();
$post->syncAttachedMedia('gallery', [
$uploaded->getKey(),
$picked->getKey(),
]);
attachMedia($file, 'cover') attaches a single file. detachMediaCollection('cover') removes that collection's links and leaves the files in the library.
MediaLibrary::resolve()->syncAttachments() performs the same replacement on either driver. On the Spatie driver the model must use InteractsWithPoolMedia.
#File URLs
PreviewUrl::for() returns the public URL, or a temporary URL when the disk is a private cloud disk.
Default driver:
use AhmedElsied\FilamentMediaBrowser\MediaLibrary;
use AhmedElsied\FilamentMediaBrowser\Support\PreviewUrl;
$library = MediaLibrary::resolve();
$cover = $library->attachments($post, 'cover')->first();
$gallery = $library->attachments($post, 'gallery');
return [
'cover' => $cover ? PreviewUrl::for($cover) : null,
'gallery' => $gallery->map(fn ($file) => PreviewUrl::for($file))->values(),
];
MediaBrowserFile::getUrl() returns the plain disk URL for a public disk.
Spatie driver:
use AhmedElsied\FilamentMediaBrowser\Support\PreviewUrl;
$cover = $post->getFirstMedia('cover');
return [
'cover' => $cover ? PreviewUrl::for($cover) : null,
'gallery' => $post->getMedia('gallery')
->map(fn ($media) => PreviewUrl::for($media))
->values(),
];
getUrl() on the Spatie media model returns Spatie's plain URL.
#Configuration
Published to config/media-browser.php.
| Key | Default | Purpose |
|---|---|---|
driver |
default |
default or spatie. Read in code with Driver::current(). |
disk |
public |
Filesystem disk for the default driver. The Spatie driver uses config('media-library.disk_name'). |
Interface strings are included in English and Arabic and follow the application locale.
#License
MIT.
#Contributing
Open an issue or a pull request on the repository. Bug reports should include the Filament version, the driver (default or spatie), and the steps that reproduce the problem.
Before you send a change:
- Fork the repository and create a branch from the default branch.
- Keep the public API stable.
MediaBrowserField,MediaLibrary, andMediaBrowserPluginare what applications call. - Test the change on the driver it touches. A change that reads or writes attachments needs to work for both drivers when both drivers share that code.
- A Filament class that moved between versions must go through
FilamentVersion, so the package still loads on Filament 3.2, 4, and 5. - Add or update the English and Arabic strings together when the interface copy changes.
- Update this README when the install steps or the public usage change.
Match the style of the surrounding code. Do not commit published config, .env files, or local vendor directories.
Featured Plugins
A selection of plugins curated by the Filament team
Blueprint
Filament Blueprint is a premium Laravel Boost extension that helps AI agents produce accurate, detailed implementation plans and security reports for Filament apps.
Filament
Custom Fields
Eliminate custom field migrations forever. Let your users create and manage form fields directly in Filament admin panels with 20+ built-in field types, validation, and zero database changes.
Relaticle
Spotlight Pro
Browse your Filament Panel with ease. Filament Spotlight Pro adds a Spotlight like Command Palette to your Filament Panel.
Dennis Koch