Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Media Library Laravel Package

lisowiecw/filament-media-library

Filament Media Library provides a ready-to-use media manager for Filament panels: upload, browse, search, and organize files, then attach media to Eloquent models. It simplifies handling images and documents in admin resources with an intuitive interface.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is the media library plus picker field: you attach existing assets to a record through a field, and the picker is the surface you're meant to build against. Start by installing it, publishing and running its migrations, and publishing its config.

The first decision, not the first line of code, is the tenant resolver. In v0.4.0 the "may this field context attach these ids" rule lives in one place, TenantReach, and the picker's validation and AttachmentReconciler both ask it. If you configure no resolver, you no longer get a silent pass that lets the database refuse the row — you get AttachRefused. Decide deliberately whether you are tenanted, because everything downstream (what the picker offers, what a reconcile accepts) reads from that one rule.

Then the first real use case: put the picker field on a form, optionally give it a thumbnailUsing() rule, upload an image, attach it. That covers the 90% path. Everything else — galleries, BlurHash, polling, drops — layers on top.

Read docs/adr/ early, particularly the three the release notes lean on. ADR 5 covers how strictly public SVG is sanitized. ADR 7 draws the line that tenancy is a policy boundary, not a query scope — which is why the picker shows one wording for every refusal. ADR 10 records that the picker field is virtual, which is why "the field's own configuration" is the promised surface and any public method not reachable from that configuration is not something you should be calling.

Implementation Patterns

Upload is the only sanitization boundary. Sanitizing runs once, at upload, on the bytes about to be written. An SVG that cannot be sanitized is refused, not stored. Do not bolt an additional sanitizer pass on afterward as belt-and-braces; you will get different results from the package's own pass and you will be surprised by which one wins. If you need stricter bytes, re-upload.

Single selection replaces; a gallery fills remaining room. Dropping a picture onto a filled single-selection cover replaces it. A gallery is measured against the room it has left. Both go through the same Drop intake module, which ingests as many files as the field can take, absorbs a refusal so one bad file doesn't cost the rest of the gesture, and hands assets back for the field to select. The practical consequence: don't write per-surface upload logic or your own "is the field full" check — the three Drop surfaces and the Upload tab already share it, and a confirm that would overflow a gallery now refuses before uploading orphans into the library.

Let the field's configuration drive the card. A card is built once per field from its thumbnailUsing() rule and asked once per asset, answering thumbnail, BlurHash, painting, and in-flight status together. The library grid and a picker's attached items now agree about the same asset. This means your customisation point is the rule you hand the field, not the grid class — and it means a tile no longer re-asks for a hash it was just handed, so don't build a parallel "fetch the hash again" path on top.

Programmatic attaches go through the reconciler and catch AttachRefused. Anything that saves around the picker — application code, queued jobs, seeders — is held to the picker's rule. AttachmentReconciler asks TenantReach exactly what the picker asks, so what the picker offers is what a reconcile accepts. Catch AttachRefused, not a query exception. unknownAsset() and tenantMismatch() are both named constructors on that one class, so a single catch covers both.

Expect refusals to be the new normal for a handful of SVGs. Documents that reference a custom entity, bare remote href/src, unquoted url(), a url() among other declarations in a style attribute (costs the whole attribute), and remote @import/url() in <style> text are now refused or stripped. Local /x and #x references survive. Re-export without the entity, or make the reference local, and re-upload.

Gotchas and Tips

composer audit was clean on 0.22 and that meant nothing. The four advisories (DTD-entity stored XSS colliding with an HTML5 named character reference, DTD attribute declaration DoS crashing libxml, mixed-case xlink:HrEf skipping the <use> nesting check, CSS injection via several vectors) are published upstream, not in the Packagist database. The constraint was moved to ^1.0 and deliberately not widened to ^0.22||^1.0, because leaving 0.22 resolvable leaves the advisories resolvable. If another dependency asks for the old line, resolve it some other way — do not re-add ^0.22.

Nothing is re-sanitized at rest. An SVG stored before this release keeps the bytes 0.22 gave it, advisories included. Re-uploading is the only path to the stricter pass. If you have a library of uploaded SVGs, that is a migration you have to schedule, not one the package does for you.

Declaring an entity is not refused; using one is. A document that declares an entity and never uses it loses the declaration and survives, as does a doctype with no declarations. So "it uploaded before and now it's refused, naming the file" almost always means a used entity or a remote reference.

The Strict pass on public placement is unchanged. It still drops an embedded image, a <style> block and a link, because the upstream allowlist still carries all three. ADR 5 notes the privacy residual it was written for is largely closed upstream, but the pass is left in force — don't read that ADR as permission to relax it.

Non-image assets render differently in a picker. An asset that isn't an image is now a bare item beside the field, matching what the grid always did, where a field with its own thumbnailUsing() rule was previously asked to paint one. Those items also no longer wait on a non-image asset whose rendering can never arrive, so a stuck-looking picker is no longer a bug to chase. This is the one behaviour change that makes the release a minor rather than a patch.

Internal methods are gone, and that is allowed. LibraryGrid::thumbnailUsing(), pollable(), isPollable(), MediaPicker::getThumbnailUrl(), paintsThroughPipeline() and MediaPicker::upload() were never on the promised surface. MediaPicker::upload() is the one most likely to bite — its work is Drop intake's now. If you were calling it, or caching a thumbnail URL you fetched through getThumbnailUrl(), move to the field's configuration. Do a grep for all six before upgrading.

Soft-deleted assets no longer block saves. Existence is asked only of the ids actually arriving, so an attachment made before its asset was deleted, before tenancy was configured, or before an asset was claimed is left alone and its tile dims. Don't write cleanup that strips attachment rows on delete in a way that fights this — a host record whose picked asset has been deleted is supposed to save again.

The picker will not tell the two refusals apart, on purpose. unknownAsset() and tenantMismatch() are distinguishable only when you are reconciling programmatically and the refusal is already settled. Distinguishing them in the picker UI would tell a viewer whether an asset they cannot reach exists, which ADR 7 forbids. Resist the urge to surface the difference to users.

Upgrade checklist for a non-tenanted app: every catch (QueryException) around an attach — especially in jobs, seeders and test helpers — now needs to catch AttachRefused instead, or alongside. The failure moved from the database to the package, and the package's version names what it would not do.

Element-dense SVGs are no longer quadratic to sanitize, so large sprite sheets and icon exports that felt slow at upload are worth re-timing before you optimise anything around them.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console