Skip to content

Models & Blade ​

Use the HasMediaLibrary trait to attach library files to any Eloquent model, and the Blade components to show them on your site.

The HasMediaLibrary trait ​

php
use Hoceineel\FilamentMediaLibrary\Concerns\HasMediaLibrary;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasMediaLibrary;
}

Files are grouped in named collections. A collection is just a string, such as gallery or downloads. The default collection is default.

MethodReturns
mediaLibraryItems(?string $collection = null)A MorphToMany relation, ordered. With no collection, every attached file.
getMediaLibraryItems(string $collection = 'default')A collection of MediaItem.
getFirstMediaLibraryItem(string $collection = 'default')The first MediaItem, or null.
getFirstMediaLibraryUrl(string $collection = 'default', string $conversion = '')The first file's URL, or null.
syncMediaLibraryItems($items, string $collection = 'default')Replaces the collection with the given ids or items, in that order.
attachMediaLibraryItem($item, string $collection = 'default')Adds one file at the end of the collection.
mediaAttachments()The raw MorphMany of attachment rows.
php
$post->syncMediaLibraryItems([4, 8, 15], 'gallery');
$post->attachMediaLibraryItem($item, 'gallery');

$post->getMediaLibraryItems('gallery');
$post->getFirstMediaLibraryItem('cover');
$post->getFirstMediaLibraryUrl('gallery', 'thumb');
$post->mediaLibraryItems('gallery')->get();

Sync only accepts ids that exist for the current tenant. Anything else is dropped without an error. Attachments are deleted with the model, except when the model uses soft deletes and is only soft deleted.

The picker field in relationship mode calls syncMediaLibraryItems() for you, using the field name as the collection unless you set another.

Avoid N+1 queries ​

getMediaLibraryItems() uses the loaded relation when it is available, so eager load it for lists:

php
$posts = Post::query()->with('mediaLibraryItems.media')->get();

foreach ($posts as $post) {
    $post->getMediaLibraryItems('gallery');
}

Storing ids in a column ​

You do not need the trait to store a selection. A picker in column mode saves ids in an attribute, and you read it with the Blade components below or with the model:

php
use Hoceineel\FilamentMediaLibrary\Models\MediaItem;

$cover = MediaItem::query()->find($post->cover_id);

Reading a file ​

Every MediaItem has these methods:

MethodReturns
url(string $conversion = '')URL of the original, or of a conversion.
thumbnailUrl()The thumb conversion, or the original for images without one.
previewUrl()The preview conversion, or the original for images without one.
conversionUrlOrOriginal(string $conversion)The conversion if it exists. For images, the original if not. For other files, null.
srcset(string $conversion = 'preview')A srcset string of responsive sizes, or an empty string.
focalPosition()A CSS object-position value such as 32% 18%. Defaults to 50% 50%.
php
$item->url();
$item->url('hero');
$item->conversionUrlOrOriginal('hero');
$item->srcset('preview');
$item->focalPosition();

Public files on a public disk get direct URLs. Files on a private disk, and files marked Only me, get short-lived signed or temporary URLs (temporary_url_minutes, 30 by default). srcset() returns an empty string for those files, and for conversions without responsive images. See Storage.

Other useful properties on the item are name, alt, caption, description, width, height, mime_type, extension and size.

Conversions ​

Conversions are presets in config/filament-media-library.php. Two ship by default:

php
'conversions' => [
    'thumb' => ['width' => 480, 'height' => 480, 'fit' => 'crop', 'format' => 'webp', 'queued' => false],
    'preview' => ['width' => 1600, 'height' => 1600, 'fit' => 'contain', 'format' => 'webp', 'queued' => true, 'responsive' => true, 'optimize' => true],
],

Each preset accepts these keys:

KeyDefaultMeaning
width, heightnoneTarget size in pixels. A missing side defaults to 4096.
fitcontaincrop fills exactly width by height and needs both. Anything else fits inside the box without cropping.
formatwebpOutput format.
queuedfalseGenerate on the queue instead of during the upload.
responsivefalseAlso build the smaller sizes used by srcset.
optimizefalseRun Spatie's image optimizers. Slow, best queued.

Add your own preset by adding a key:

php
'hero' => ['width' => 2400, 'height' => 1000, 'fit' => 'crop', 'queued' => true],

New uploads get the conversion. Build it for existing files with:

bash
php artisan media-library:regenerate --only=hero --missing

Cropped conversions centre on the focal point set in the inspector. When a focal point changes, the file's conversions are rebuilt on the queue, so keep a worker running. See Commands.

Conversions are built for images. A non-image file normally has none, and then thumbnailUrl() and conversionUrlOrOriginal() return null.

Blade components ​

Both components are registered by the package. Outside Filament panels, enable standalone mode and load Filament's assets first: see Outside Panels.

x-media-library-image ​

blade
<x-media-library-image
    :item="$post->cover_id"
    conversion="preview"
    sizes="(min-width: 1024px) 50vw, 100vw"
    class="aspect-video w-full rounded-xl object-cover"
/>
AttributeDefaultMeaning
itemnoneA MediaItem, an id, or null. With null or a missing file, nothing is rendered.
conversionpreviewWhich conversion to use. Uses the original if it does not exist.
sizes100vwThe sizes attribute. Only output when there is a srcset.
lazytrueAdds loading="lazy" and decoding="async". Pass :lazy="false" for images above the fold.

Any other attribute, such as class or id, is passed to the <img>. The component also sets:

  • alt from the file's alt text, or an empty string.
  • width and height from the original file. Add object-cover and a fixed ratio class when you use a cropped conversion.
  • srcset from the responsive sizes, when the conversion has them.
  • style="object-position: ..." from the focal point, so a cropped image keeps the subject in frame.

Passing an item you already loaded avoids a query: :item="$post->getFirstMediaLibraryItem('cover')".

blade
<x-media-library-gallery :record="$post" collection="gallery" :columns="3" aspect-ratio="4:3" />

<x-media-library-gallery :items="$post->gallery_ids" :lightbox="false" :captions="false" />
AttributeDefaultMeaning
recordnoneA model with HasMediaLibrary. Use with collection, or with attribute for a column.
collectionnoneAttachment collection to show.
attributenoneColumn on record holding the ids. Use it instead of collection.
itemsnoneA list of ids, a single id, or an Eloquent collection of items.
conversionthumbConversion used for the tiles.
columns4Grid columns.
aspect-ratio1:1Ratio of each tile.
captionstrueShow the caption, or the name, under each tile.
lightboxtrueOpen files full screen on click.

Give it either items, or record with collection or attribute. A record with neither shows nothing. The items form only shows files the current user can see, so other people's private files are left out.

The gallery uses Alpine for the lightbox. Pages built with @filamentScripts already have it. It is the same grid as the infolist entry.

Commercial licence. One licence per production project. Terms · Privacy · Refunds