georgemosesgroup / filament-media-library
A powerful media library package for Filament v3/v4/v5+ with MediaPicker form component, drag & drop uploads, and folder management
Package info
github.com/georgemosesgroup/FilamentMediaLibrary
Language:Blade
pkg:composer/georgemosesgroup/filament-media-library
Requires
- php: ^8.1|^8.2|^8.3|^8.4
- filament/filament: ^3.0|^4.0|^5.0
- illuminate/contracts: ^10.0|^11.0|^12.0
- league/flysystem-aws-s3-v3: ^3.0
- spatie/laravel-package-tools: ^1.14
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
README
A powerful media library package for Filament with MediaPicker form component, drag & drop uploads, and full integration with FileManager.
Requirements
| Package | Version |
|---|---|
| PHP | 8.1+ |
| Laravel | 10.x, 11.x, 12.x |
| Filament | 3.x, 4.x, 5.x |
Features
- Media Library Page - Full standalone file manager in navigation
- MediaPicker Form Component - Select files from library or upload new ones
- Two Modes - Browse-only (default) or Upload mode
- Browse Library Modal - Full file browser with folder navigation
- Single & Multiple Selection - Support for cover images and galleries
- Gallery Grid Layout - Unified container with customizable columns and height
- Interactive Media Players - Built-in video and audio players in grid mode
- Auto Directory Structure - Automatically creates folder hierarchy based on resource name
- Multi-tenancy Support - Full tenant isolation out of the box
- Filament Plugin - Easy registration with customizable navigation
Installation
composer require georgemosesgroup/filament-media-library
Quick Install (Recommended)
Run the install command to automatically configure everything:
# Basic installation (local storage) php artisan filament-media-library:install # With DigitalOcean Spaces php artisan filament-media-library:install --storage=do_spaces # With AWS S3 php artisan filament-media-library:install --storage=s3
This will:
- Publish the config file
- Run migrations
- Configure storage (DO Spaces/S3 if selected)
- Configure your Filament theme CSS for Tailwind
Then rebuild your theme:
npm run build
Manual Installation
1. Register Plugin
Add the plugin to your Filament panel in AdminPanelProvider.php:
use Alura\FilamentMediaLibrary\FilamentMediaLibraryPlugin; public function panel(Panel $panel): Panel { return $panel // ... ->plugins([ FilamentMediaLibraryPlugin::make(), ]); }
This automatically adds the Media Library page to your navigation.
2. Publish Config (Optional)
php artisan vendor:publish --tag="filament-media-library-config"
3. Run Migrations
php artisan migrate
4. Publish Assets
The package includes its own CSS for hover effects and animations. Publish the assets:
php artisan filament:assets
Note: This step is required after installation or updating the package. The CSS is automatically registered with Filament's asset system.
5. Configure Custom Theme (Optional - for Media Library Page)
If you're using the Media Library page (not just MediaPicker), add the package views to your Filament theme CSS (resources/css/filament/admin/theme.css):
@source '../../../vendor/georgemosesgroup/filament-media-library/resources/views/**/*';
Then rebuild your theme:
npm run build
Plugin Options
Customize the Media Library page:
FilamentMediaLibraryPlugin::make() ->navigationGroup('Content') // Group in navigation ->navigationSort(10) // Sort order ->navigationIcon('heroicon-o-folder') // Custom icon ->navigationLabel('Files') // Custom label
Disable the page (use only MediaPicker component):
FilamentMediaLibraryPlugin::make() ->disableMediaLibraryPage()
Configuration
Config file: config/filament-media-library.php
Key Settings
return [ // Storage disk 'disk' => env('MEDIA_LIBRARY_DISK', 'public'), // Upload limits 'upload' => [ 'max_file_size' => 100 * 1024 * 1024, // 100MB 'max_files_per_upload' => 50, ], // Multi-tenancy 'multi_tenancy' => [ 'enabled' => true, 'tenant_column' => 'tenant_id', ], // Modal appearance 'picker' => [ 'default_position' => 'slide-over', // 'center' or 'slide-over' 'default_width' => 'md', ], ];
Cloud Storage (S3 / DigitalOcean Spaces)
The package fully supports S3-compatible cloud storage including AWS S3 and DigitalOcean Spaces.
1. Configure Filesystem Disk
Add to config/filesystems.php:
'disks' => [ // ... other disks // DigitalOcean Spaces 'do_spaces' => [ 'driver' => 's3', 'key' => env('DO_SPACES_KEY'), 'secret' => env('DO_SPACES_SECRET'), 'region' => env('DO_SPACES_REGION', 'sfo3'), 'bucket' => env('DO_SPACES_BUCKET'), 'url' => env('DO_SPACES_URL'), 'endpoint' => env('DO_SPACES_ENDPOINT'), 'use_path_style_endpoint' => false, 'visibility' => 'public', ], // AWS S3 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), 'visibility' => 'public', ], ],
2. Environment Variables
For DigitalOcean Spaces:
DO_SPACES_KEY=your-key DO_SPACES_SECRET=your-secret DO_SPACES_REGION=sfo3 DO_SPACES_BUCKET=your-bucket-name DO_SPACES_URL=https://your-bucket-name.sfo3.digitaloceanspaces.com DO_SPACES_ENDPOINT=https://sfo3.digitaloceanspaces.com MEDIA_LIBRARY_DISK=do_spaces
For AWS S3:
AWS_ACCESS_KEY_ID=your-key AWS_SECRET_ACCESS_KEY=your-secret AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET=your-bucket-name AWS_URL=https://your-bucket-name.s3.amazonaws.com MEDIA_LIBRARY_DISK=s3
3. Set Media Library Disk
Update your .env:
MEDIA_LIBRARY_DISK=do_spaces # or 's3' for AWS
Features
- Automatic thumbnail generation - Works seamlessly with cloud storage
- Public URLs - Files are publicly accessible via CDN
- Visibility control - Set 'public' or 'private' visibility per file
- Temporary URLs - Private files use signed temporary URLs (S3 feature)
Usage
Basic Usage (Browse Mode - Default)
By default, MediaPicker shows a "Browse Library" button to select from existing files:
use Alura\FilamentMediaLibrary\Forms\Components\MediaPicker; public static function form(Form $form): Form { return $form->schema([ // Single image - browse from library MediaPicker::make('cover_image_id') ->label('Cover Image') ->images(), // Multiple images - browse from library MediaPicker::make('gallery_ids') ->label('Gallery') ->images() ->multiple() ->maxFiles(10), ]); }
Upload Mode
Enable drag & drop upload with allowUpload():
MediaPicker::make('cover_image_id') ->label('Cover Image') ->images() ->allowUpload() // Enable drag & drop upload ->directory('Products/{record_id}/Cover')
Model Setup
Minimum required:
class Product extends Model { protected $fillable = [ 'name', 'cover_image_id', // integer - single file 'gallery_ids', // json - multiple files ]; protected function casts(): array { return [ 'gallery_ids' => 'array', // Required for multiple files ]; } }
Migration:
Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('cover_image_id')->nullable()->constrained('file_items')->nullOnDelete(); $table->json('gallery_ids')->nullable(); $table->timestamps(); });
Auto Directory Structure
Automatically organize uploads into folders based on resource:
MediaPicker::make('cover_image_id') ->images() ->directory('Products/{record_id}/Cover')
Available placeholders:
| Placeholder | Description | Example |
|---|---|---|
{resource} |
Resource name | Products |
{record_id} |
Record ID | 123 |
{field} |
Field name | cover_image_id |
{date} |
Current date | 2026-01-28 |
{year} |
Current year | 2026 |
{month} |
Current month | 01 |
{tenant_id} |
Tenant ID | 1 |
Example folder structure:
Blog Posts/
├── 1/
│ ├── Cover/
│ │ └── image1.jpg
│ └── Gallery/
│ ├── photo1.jpg
│ └── photo2.jpg
└── 2/
└── Cover/
└── image2.jpg
Component Options
File Type Filters
// Only images MediaPicker::make('image_id')->images() // Only videos MediaPicker::make('video_id')->videos() // Only documents MediaPicker::make('document_id')->documents() // Custom mime types MediaPicker::make('file_id')->acceptedTypes(['application/pdf', 'image/png'])
Selection Mode
// Single file (default) MediaPicker::make('cover_id') // Multiple files MediaPicker::make('gallery_ids') ->multiple() ->maxFiles(20) ->minFiles(1)
Modal Customization
MediaPicker::make('image_id') ->modalPosition('center') // 'center' or 'slide-over' ->modalWidth('3xl') // sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl
Upload Settings
MediaPicker::make('image_id') ->directory('Uploads/{date}') // Auto folder structure ->autoCreateDirectory(true) // Create folders if not exist ->maxFileSize(10 * 1024 * 1024) // 10MB limit
Visibility & State
MediaPicker::make('image_id') ->required() ->disabled() ->hidden(fn () => !auth()->user()->canUpload())
Gallery Layout (Grid Mode)
Display multiple files in a customizable grid layout:
// Preset gallery layouts with configurable parameters MediaPicker::make('gallery_ids') ->multiple() ->gridGallery() // Default: 3 columns, 120px height, cover ->gridGallery(4) // 4 columns ->gridGallery(4, '150px') // 4 columns, 150px height ->gridGallery(4, '150px', 'contain') // Full customization // Other presets MediaPicker::make('gallery_ids') ->multiple() ->compactGallery() // 2 columns, 150px height ->thumbnailGallery() // 4 columns, 100px height ->thumbnailGallery(6, '80px') // 6 columns, 80px height // Manual configuration MediaPicker::make('gallery_ids') ->multiple() ->previewColumns(4) // Number of columns (1-6) ->previewMaxHeight('150px') // Height of each item ->previewImageFit('cover') // 'cover', 'contain', or 'fill'
Grid Mode Features:
- Unified container with files and "Add" button together
- Drag & drop directly onto the grid area with prominent overlay
- Interactive video player with play/pause
- Interactive audio player with purple gradient background
- Hover effects on cards with ring highlight
- Remove button appears on hover (red on hover)
- Smooth animations and transitions
Interactive Media Players
In grid mode, media files have built-in players:
Video:
- Click play button to start video
- Native controls appear (play, pause, progress, volume, fullscreen)
- Thumbnail shown when paused
Audio:
- Purple gradient background
- Click to play/pause
- Shows filename
How It Works
Two Modes
-
Browse Mode (default) - Click "Browse Library" button
- Opens modal with full file browser
- Navigate folders, search files
- Select existing files from library
-
Upload Mode - Enable with
->allowUpload()- Shows drag & drop zone
- Files upload immediately
- Stored in configured directory
- ID saved to form field
Data Storage
Single file: Stores integer ID
'cover_image_id' => 42
Multiple files: Stores JSON array of IDs
'gallery_ids' => [42, 43, 44]
Optional: Helper Methods
Add these to your model for convenient access:
class Product extends Model { // ... fillable and casts ... // Relationship for eager loading public function coverImage(): BelongsTo { return $this->belongsTo(FileItem::class, 'cover_image_id'); } // Get cover URL public function getCoverUrl(): ?string { return $this->coverImage?->getUrl(); } // Get gallery images collection public function getGalleryImages() { if (empty($this->gallery_ids)) { return collect(); } return FileItem::whereIn('id', $this->gallery_ids)->get(); } // Get gallery URLs array public function getGalleryUrls(): array { return $this->getGalleryImages() ->map(fn($img) => $img->getUrl()) ->toArray(); } }
API Usage (Frontend)
Get file data for API responses:
// In API Resource or Controller public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'cover_image' => $this->coverImage ? [ 'id' => $this->coverImage->id, 'url' => $this->coverImage->getUrl(), 'thumbnail' => $this->coverImage->getThumbnailUrl(), ] : null, 'gallery' => $this->getGalleryImages()->map(fn($img) => [ 'id' => $img->id, 'url' => $img->getUrl(), 'thumbnail' => $img->getThumbnailUrl(), ]), ]; }
Multi-tenancy
The package automatically handles tenant isolation:
- All files are scoped to current tenant
- Folder structure is tenant-specific
- Users can only see their tenant's files
Tenant is resolved from (in order):
- Container-bound
tenant - Filament
Filament::getTenant() - Authenticated user's
tenant_id - Session
filament_tenant_id
Synchronization with FileManager
This package uses the same database tables as FileManager:
file_folders- Folder structurefile_items- File recordsfile_tags- File tagsfile_versions- File versionsfile_shares- Shared links
Files uploaded via MediaPicker appear in FileManager and vice versa.
Artisan Commands
Install Command
# Basic installation php artisan filament-media-library:install # With DigitalOcean Spaces storage php artisan filament-media-library:install --storage=do_spaces # With AWS S3 storage php artisan filament-media-library:install --storage=s3 # Skip theme configuration php artisan filament-media-library:install --skip-theme
Migrate Storage Command
Migrate files between storage disks (e.g., local to cloud):
# Dry run (preview what will be migrated) php artisan media-library:migrate-storage --from=public --to=do_spaces --dry-run # Actual migration php artisan media-library:migrate-storage --from=public --to=do_spaces # Delete source files after migration php artisan media-library:migrate-storage --from=public --to=do_spaces --delete-source
Updating the Package
After updating via composer, run:
composer update georgemosesgroup/filament-media-library php artisan filament:assets php artisan view:clear
UI/UX Features
Drag & Drop Upload
When allowUpload() is enabled:
- Clear "Drop to upload" overlay when dragging files
- Backdrop blur effect for better visibility
- Prominent border highlight
Card Hover Effects
- Ring highlight on card hover
- Remove button hidden by default, appears on hover
- Remove button turns red when hovered
- Subtle overlay on image cards
Upload Progress
- Animated progress bar with glow effect
- Shimmer animation during upload
- Success checkmark with green glow
- Error state with retry button
Responsive Design
All components are fully responsive and work on mobile devices.
Troubleshooting
Upload returns 500 error
Check Laravel log for details:
tail -f storage/logs/laravel.log
Common issues:
- tenant_id NULL - User not authenticated or tenant not resolved
- Permission denied - Check storage folder permissions
- File too large - Increase
upload_max_filesizein php.ini
Files not showing
- Clear cache:
php artisan cache:clear - Check tenant scope is correct
- Verify files exist in database
Modal not opening
- Check browser console for JS errors
- Ensure Alpine.js is loaded
- Clear Filament cache:
php artisan filament:cache-components
License
MIT License - see LICENSE file for details.