Media Browser plugin screenshot
Dark mode ready
Multilingual support
Supports v5.x

Media Browser by Ahmed Elsayed

Community

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.

Tags: Forms Form Field Spatie Integration
Supported versions:
5.x 4.x 3.x
Ahmed Elsayed avatar Author: Ahmed Elsayed

Package health

Automated checks of this plugin's Composer package

100 / 100
Security 100
Maintenance 100
Ecosystem 100
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.lock is 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.4 supports current PHP 8.5.
  • Skipped: Current Symfony version supported
Third-party plugin. This is built by the community, not the Filament team. Filament does not review, endorse, or vet the security of plugins outside the 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 .
Powered by Plumb Last scanned 12 hours ago

Documentation

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

  • 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:

  1. Fork the repository and create a branch from the default branch.
  2. Keep the public API stable. MediaBrowserField, MediaLibrary, and MediaBrowserPlugin are what applications call.
  3. 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.
  4. A Filament class that moved between versions must go through FilamentVersion, so the package still loads on Filament 3.2, 4, and 5.
  5. Add or update the English and Arabic strings together when the interface copy changes.
  6. 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.

The author

Ahmed Elsayed avatar Author: Ahmed Elsayed

I'm a software engineer with 7 years building scalable, secure systems in NestJS and Laravel. I like hard problems and teams that ship.

Plugins
1
Stars
1