georgemosesgroup/filament-media-library

A powerful media library package for Filament v3/v4/v5+ with MediaPicker form component, drag & drop uploads, and folder management
4
Install
composer require georgemosesgroup/filament-media-library
Latest Version:1.2.5
PHP:^8.1|^8.2|^8.3|^8.4
Maintainer: georgemosesgroup

Filament Media Library

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

  1. Browse Mode (default) - Click "Browse Library" button

    • Opens modal with full file browser
    • Navigate folders, search files
    • Select existing files from library
  2. 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:

  1. All files are scoped to current tenant
  2. Folder structure is tenant-specific
  3. Users can only see their tenant's files

Tenant is resolved from (in order):

  1. Container-bound tenant
  2. Filament Filament::getTenant()
  3. Authenticated user's tenant_id
  4. Session filament_tenant_id

Synchronization with FileManager

This package uses the same database tables as FileManager:

  • file_folders - Folder structure
  • file_items - File records
  • file_tags - File tags
  • file_versions - File versions
  • file_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_filesize in php.ini

Files not showing

  1. Clear cache: php artisan cache:clear
  2. Check tenant scope is correct
  3. Verify files exist in database

Modal not opening

  1. Check browser console for JS errors
  2. Ensure Alpine.js is loaded
  3. Clear Filament cache: php artisan filament:cache-components

License

MIT License - see LICENSE file for details.