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 Curator Laravel Package

awcodes/filament-curator

Filament Curator is a media picker and manager plugin for Filament Admin, offering a full media library UI with Glide-signed image URLs and a one-command installer. Supports Filament 3, 4, and 5. Note that it does not work with Spatie Media Library.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Curator is a presentation-layer plugin, not an infrastructure component. It sits entirely inside the Filament admin panel: one Media Eloquent model, one resource, one form field (CuratorPicker), one table column (CuratorColumn), and a Blade component (<x-curator-glider>). It introduces no queue, no service, no daemon, and no external dependency beyond league/glide for image transforms. For a TPM this is the most important characteristic: the integration surface is narrow, the reversal cost is low, and it does not reshape the application's domain model.
  • The architectural boundary is a single media table plus foreign keys on consuming models. Single relations are belongsTo(Media::class, 'featured_image_id'); multi-media is a belongsToMany with a pivot order column. That is a conventional, inspectable schema — any competent Laravel team can query, back up, and migrate it without the package.
  • The hard architectural constraint is the explicit incompatibility with Spatie Media Library. These two solve the same problem differently (Spatie attaches media polymorphically to any model; Curator centralizes media and points at it by ID). A codebase that already standardizes on Spatie cannot adopt Curator incrementally — it is an either/or decision per model, and mixing both creates two media sources of truth, two storage conventions, and duplicated admin UX. Confirm this first; it is the single largest disqualifier.
  • Media delivery is routed through the application: URLs are signed with a Glide token, and the media route rejects mismatched signatures. That is good for access control and hotlink prevention, but it means image serving becomes an application-tier responsibility rather than a static-file or CDN concern. This shifts load-bearing behavior onto PHP workers and is the main thing to pressure-test with the platform/ops owner.

Integration feasibility

  • Integration effort is genuinely small: composer require, php artisan curator:install, register CuratorPlugin on the panel, add CuratorPicker fields and model relations. The curator:install command handles token generation, which removes the most common class of setup error.
  • Two non-obvious setup steps that repeatedly bite teams and should be explicit tickets, not "read the README":
    1. Custom Filament theme required. The plugin's plugin.css must be imported and its Blade views added to Tailwind @source in the theme CSS. Without this, the picker renders unstyled. Teams on a stock panel will hit this on day one.
    2. Standalone forms users must add <x-curator::modals.modal /> to the layout before </body>. Panels users do not need this. Knowing which mode the project is in determines whether this is a zero-step or a one-step task.
  • The field API is deliberately Filament-native: maxWidth, minSize, maxSize, rules, acceptedFileTypes, disk, visibility, directory, imageCropAspectRatio, plus multiple() and relationship(). Existing FileUpload knowledge transfers, so developer ramp-up is measured in hours, not days.
  • Extensibility hooks are present and clean: PathGenerator is an interface you can implement (DefaultPathGenerator, DatePathGenerator, UserPathGenerator ship in-box), and Curation::presets() registers reusable crop/format/quality presets. This is enough to satisfy typical multi-tenant and per-user storage layouts without forking.
  • tenantAware() defaults to true on the picker. For a multi-tenant product this is a feature, but it must be verified against however tenancy is actually implemented in the panel — default-on behavior that silently scopes media is exactly the kind of thing that produces "the image disappeared" tickets.

Technical risk

  • Token lifecycle is the top operational risk. CURATOR_GLIDE_TOKEN is written to the local .env, which is gitignored. Every environment — teammate checkout, CI, staging, production — needs its own value, and the README is explicit that a missing variable breaks both URL generation and the media route. The value need not match across environments, which contains the blast radius, but it must be present. Mitigation is trivial and mandatory: add CURATOR_GLIDE_TOKEN= to .env.example and to the secret manager before the first deploy.
  • Rotating the token invalidates previously signed URLs. Re-running curator:token overwrites the value, so any URL that outlived its request — cached HTML, CDN copies, sent emails, URLs pasted into stored rich content — starts returning 403. If config is cached, php artisan config:clear is required. This is a real failure mode with a real user-visible symptom, and it turns token rotation into a change-management event rather than a routine operation. Treat it like a key rotation with a defined communication and cache-invalidation window.
  • Upload security posture is better than average but not free. Curator's default accepted types are the full MimeType list minus executable/script-serving content (text/html, text/javascript, application/xml, application/x-httpd-php, application/octet-stream, and similar), because media is served from your own origin and, on the public disk, sits inside the document root under the storage symlink. Only SVG is sanitized; anything else opted back in is stored and served verbatim. The route sets X-Content-Type-Options: nosniff and forces Content-Disposition: attachment for restricted types, but those headers do not apply to direct storage symlink access. The safe posture is: keep defaults, do not re-enable HTML/XML/JS, and if a business requirement forces it, serve from a private disk. This deserves a one-line security sign-off, not a full review.
  • Delivery path is synchronous PHP. force on the Glider component is documented as necessary for cloud disks but with "performance implications." Any plan that routes all production image traffic through the app rather than a CDN edge should be load-tested before launch, not after.
  • N+1 is a known, documented trap. CuratorColumn on a relationship will N+1 unless the table query eager-loads (->with(['featured_image', 'product_pictures']) or modifyQueryUsing). Cheap to fix, easy to forget, and invisible until a list view gets slow.

Key questions

  1. Does the codebase already use Spatie Media Library anywhere, and is there appetite to run two media systems? If yes to the first, this package is out.
  2. Which disk and visibility will production media live on — public with the storage symlink, or a private/cloud disk? The answer determines whether the documented header protections actually apply.
  3. Is there a CDN or edge cache in front of the app? If so, who owns cache invalidation when the Glide token rotates, and is a 403 storm an acceptable rotation outcome?
  4. Which Filament major version is the fleet on, and does the pinned Curator major match? (Package 5.x supports Filament 4.x and 5.x, which materially de-risks the Filament upgrade.)
  5. What is the expected media volume and image-request rate, and has anyone modeled app-tier serving at that rate?
  6. Is multi-tenancy in play, and does tenantAware() default-on align with the existing tenancy model?
  7. Who owns this after the sprint — is there a named maintainer for media, or does it become orphaned admin functionality?

Integration Approach

Stack fit

  • Prerequisite stack is Laravel + Filament Panels (or the standalone Filament forms package). If the admin surface is Filament, the fit is close to ideal; if it is Nova, Blade-only, or Livewire-without-Filament, the package provides no path at all and the picker/column/glider trio is unavailable — the decision is effectively moot.
  • Fit is strongest where the product needs a shared editorial image library with reusable crops — CMS pages, marketing content, product galleries, blog imagery. It is weakest where media is per-record and disposable with no cross-record reuse; there the centralized-library model adds a browsing step with no payoff.
  • The Glider/Glide layer is a good match for teams that want responsive srcset and WebP output without adopting a full image CDN (Imgix, Cloudinary). It is a poor match for teams that have already committed to one of those, since it duplicates the transform layer.
  • Custom theme requirement means the front-end build pipeline must be one the team controls. Projects with a vendored, rarely-rebuilt theme will find the CSS import step disproportionately annoying.

Migration path

  • Greenfield is the easy case: install, register plugin, add fields, done. Sequence it as an early-sprint task so downstream resources are built against it rather than retrofitted.
  • Brownfield from native FileUpload + a string column is a moderate data migration: you must move existing files into Curator's configured disk/directory, create Media rows, and rewrite FK columns to point at media IDs. PathGenerator selection matters here — choosing DatePathGenerator or UserPathGenerator after the fact means the existing files land in the wrong place. Decide the path generator before writing the migration, not after.
  • Brownfield from Spatie Media Library is not a migration, it is a rewrite of every model's media relation plus a file relocation plus a re-render of every stored URL. Price it as such, or decline it. The package's own warning is unambiguous.
  • Path generators are the escape hatch for organizations with an existing storage convention (per-tenant, per-user, dated). Implement PathGenerator rather than fighting the config.
  • Reverse migration is realistic: the media table and FKs are plain schema, and files live on a normal disk. Exiting Curator means writing a backfill to whatever comes next — annoying, but bounded and non-destructive. This low lock-in is the strongest argument for approving it.

Compatibility

  • Version matrix is clean and per-major aligned (1.x→Filament 2, 2.x→Filament 2, 3.x→Filament 3, 4.x→Filament 4, 5.x→Filament 4 & 5). That last row is the notable one: package 5.x straddling two Filament majors reduces the risk that a Filament upgrade forces a media-layer rewrite in the same window.
  • MIT license — no procurement, no legal review, no per-seat cost. Contributes to a low-ceremony approval.
  • The Spatie incompatibility is a compatibility constraint in the truest sense and belongs in the ADR as an explicit, dated decision, so a future team does not unknowingly introduce Spatie alongside it.
  • Third-party plugin means compatibility is not on your release cadence. A Filament minor release can break the picker's styling or behavior, and your only remedy is to wait or pin. Pin to a known-good Curator major and treat upgrades as deliberate.

Sequencing

  1. Week 0 — spike and gate. Install on a throwaway branch, confirm Spatie status, confirm Filament version alignment, verify custom theme build succeeds with the plugin CSS and @source entries. Kill or proceed here; do not proceed on optimism.
  2. Week 0 — environment plumbing. Add CURATOR_GLIDE_TOKEN= to .env.example and to every environment's secret store before any feature work. Decide the path generator. Document the config-cache step. This is a 30-minute task that prevents a production incident.
  3. Week 1 — plugin registration and one vertical slice. Register CuratorPlugin with label/navigation/group, add a single CuratorPicker on one resource, wire the belongsTo relation, add a CuratorColumn with eager loading. Ship it behind a feature flag if the admin is user-facing.
  4. Week 2 — breadth. Roll the picker across remaining resources; add the multi-media belongsToMany + pivot order where galleries are needed; add AttachCuratorMediaPlugin to any RichEditor fields. Register curation presets for the handful of sizes the design system actually uses — resist inventing a preset per screen.
  5. Week 3 — delivery hardening. Adopt <x-curator-glider> with srcset/sizes in Blade views, confirm lazy loading defaults are acceptable, load-test the media route if traffic is nontrivial, and decide on CDN vs. app-served.
  6. Week 4 — data migration (if brownfield). Backfill files and Media rows, rewrite FKs, verify no orphaned references, run the N+1 check on every list view touched.
  7. Ongoing — upgrade and rotation runbooks. One page each: how to bump Curator with Filament, and how to rotate the Glide token without a 403 storm.

Operational Impact

Maintenance

  • Maintenance load is low but not zero, and it is concentrated in two places: (a) keeping Curator major aligned with the Filament major on upgrade windows, and (b) owning the token. Everything else — media CRUD, cropping, curation presets — is admin user activity, not engineering toil.
  • No scheduled jobs, no migrations beyond the install publish, no background workers. The package adds a resource to a panel you already maintain. That is close to the minimum possible operational footprint for a media system.
  • The real ongoing cost is upgrade coupling: Curator is a passenger on Filament's release train. Budget a small amount of engineering time per Filament upgrade to re-verify the picker, column, and theme CSS.
  • 440 stars and 0 dependents is the honest signal here: this is a single-maintainer, single-author ecosystem package, not a foundation library. It is well-documented and clearly maintained (recent releases, careful README, explicit security notes), but bus factor is one. Mitigate by pinning the version, keeping the custom PathGenerator implementations in-house, and accepting that a fork is a plausible long-term outcome. For a panel plugin with this narrow a surface, that is an acceptable risk — for a payments or auth library it would not be.

Support

  • Support model is community/author-driven via GitHub issues. No SLA, no vendor contract, no paid tier. Internal ownership is the substitute: name one engineer accountable for the media layer, with the ADR and runbook living in the repo.
  • Documentation quality is above average for the ecosystem — compatibility matrix, install caveats, security rationale for the MIME exclusions, and explicit warnings about token rotation and Spatie. That documentation is itself the support plan; the team can self-serve most questions.
  • Escalation path for a blocking bug is: pin to previous version, patch locally, or fork. All three are viable precisely because the package is small and MIT. Verify that assumption in the spike by reading the source, not just the README.

Scaling

  • Metadata scaling is a non-issue. A media table with a few hundred thousand rows is unremarkable for Postgres/MySQL with sensible indexing on the FK columns consuming it.
  • Delivery scaling is the real question. Every rendered image is a request to your PHP app unless a CDN absorbs it. Glide transforms are CPU-bound and, by default, uncached beyond whatever your stack provides. At low-to-moderate editorial traffic this is invisible. At high image-request volume it competes with application requests for the same workers. Model this before launch, not after, and if the numbers are uncomfortable, put a CDN in front or pre-generate the sizes the design system needs via curation presets rather than transforming on demand.
  • srcset support is a genuine scaling asset: serving appropriately sized images rather than one large original is the single biggest lever on bandwidth and perceived performance.
  • Multi-tenant installs should verify tenantAware() behavior under load before assuming isolation is both correct and efficient — default-on scoping is convenient but should be confirmed rather than trusted.

Failure modes

Failure Symptom Likelihood Mitigation
CURATOR_GLIDE_TOKEN missing in an environment URL generation and media route both fail — images dead High on first deploy to a new env Add to .env.example and secret store before deploy
Token rotated (curator:token re-run) Cached HTML, CDN copies, sent emails, stored content return 403 Medium Treat as key rotation: schedule, purge caches, run config:clear
Token changed but config cached Stale token served; signatures rejected Medium Bake php artisan config:clear into the rotation runbook
Custom theme not built with plugin CSS / @source Unstyled, broken picker UI High on first install Explicit spike acceptance criterion
Standalone forms without modal in layout Picker modal never opens Medium (only standalone) Checklist item if not on Panels
Relationship column without eager loading Slow list views as media grows High if undocumented ->with(...) or modifyQueryUsing on every affected table
Executable MIME types opted back in Stored XSS via uploaded HTML/XML served from own origin Low but severe Keep MimeType::defaults(); private disk if unavoidable
Direct storage symlink access bypassing route headers Restricted type served without nosniff/attachment Low Private disk for anything sensitive
Filament minor upgrade Picker/column styling or behavior regressions Medium over time Pin Curator major; re-verify on Filament upgrades
Maintainer abandonment No upstream fixes Low-to-medium over years Pin, keep path generators in-house, fork as fallback

Ramp-up

  • Developer ramp-up is genuinely fast: half a day to productive, one to two days to fluent. The API mirrors Filament's FileUpload, so prior Filament experience transfers almost entirely. The three things a developer must learn are the plugin registration surface, the relationship patterns (single belongsTo vs. multi belongsToMany with pivot order), and the Glider component's attribute set.
  • Admin/editor ramp-up is a UX question rather than a technical one: users get an actual media library with browse, upload, crop, and reuse. That is usually a net reduction in editor support tickets versus ad-hoc per-form uploads — but it does introduce a new mental model ("the library" vs. "my upload box") that warrants a short internal walkthrough and a naming convention for assets.
  • Ops ramp-up requires exactly two artifacts, both of which should be written during the integration sprint rather than after the first incident: a token rotation runbook and an environment provisioning checklist covering the token, the storage symlink, and the config cache. Neither is long. Both are the difference between a routine operation and a 403 outage.
  • Overall TPM verdict: green-light with conditions — conditions being (1) confirmed absence of Spatie Media Library, (2) Glide token provisioned across all environments before feature work, (3) explicit acceptance of single-maintainer risk with a pinning strategy, and (4) a load-test of the media route if image traffic is material. The blast radius is a panel plugin, the schema is plain, and exit cost is low. That combination makes the residual risk easy to justify.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core