artisanpack-ui/media-library
A Laravel media library for handling uploads, storage, and image conversions, with a UI-friendly API for attaching and managing files across your application.
MediaUploaded queueable event decouples post-upload work (AI alt-text generation, virus scanning, transcription, CDN warm-up, webhook fan-out) from the request lifecycle. This turns "can we afford to do X on upload?" into a scoping question rather than an architecture debate — you can commit to enrichment features in a roadmap without re-planning the upload path each time.pending → optimized / failed). v1.6.0 extends that same buy case to the frontend: React and Vue useMediaPicker composables, useMediaLibrary, and a MediaModal with a controllable fetch contract (enabled, autoFetch). The undifferentiated plumbing you would otherwise build and maintain now spans both tiers — picker state, modal lifecycle, fetch deferral, cache-on-reopen — not just upload dispatch and image optimization.optimization block on MediaResource (with TypeScript types OptimizationStatus and MediaOptimization) is the data contract behind an admin "media health" surface: status badges, bytes-saved reporting, generated-format inventory, and failure triage. It makes a "storage cost / asset quality" initiative plannable in slices — badge first, dashboards second — without backend changes between slices.enabled option defers the picker's initial media and recently-used fetches until the picker is actually needed, and MediaModal passes its open state through automatically. That converts "our admin pages fire /api/media requests on every load, even for a closed modal" from a refactor into a prop. It is the enabler for embedding the picker in dashboards, tabs, drawers, and heavy pages without an API-load tax — and it gives you a concrete before/after metric (media requests per page load) for a perf initiative.media:backfill-optimization-status is a migration/rollout enabler on day one. Any adoption of this version on an existing library is a one-command data migration rather than a "legacy assets look broken" incident, which lowers the cost and risk of scheduling the upgrade.optimization_bytes_saved / optimization_original_size give a directly reportable metric ("storage saved"), and the v1.6.0 fetch-deferral behavior gives a companion "wasted API calls eliminated" figure, which supports a launch narrative and post-launch measurement without instrumenting the pipeline or the client yourself.useMediaLibrary honouring autoFetch: false under React StrictMode re-runs, and the Vue composable no longer swallowing the first filter change when autoFetch is false — remove the two most likely "we'll have to patch the hooks" line items for teams on React 18+ StrictMode or building custom controlled filter UIs. That materially lowers the effort estimate in a build-vs-buy comparison.Consider adoption when most of the following hold:
enabled option is a direct, low-effort win — and it applies equally in React and Vue.autoFetch: false) or run React StrictMode in development, v1.6.0 fixes the specific behaviors that otherwise force workarounds.ShouldQueue listeners, and the backfill command is run as part of deployment — not after.Look elsewhere (or stay on a narrower vendor) when:
/api/media shape. The escape hatch is now better (use the composables standalone with enabled / autoFetch and render your own UI), but if a deeply customized picker is a hard requirement, still budget design and frontend work, just less than before.To executives (30 seconds): Two releases now cover both halves of the media story. v1.5.0 removed the main reason we would have delayed enrichment features: post-upload work — AI alt-text, moderation, indexing — runs in the background instead of slowing uploads, and every image carries a persisted "optimized / pending / failed" status with bytes saved. v1.6.0 fixes the admin side: the media picker no longer fires API requests until it is actually opened, so we can put it in dashboards and modals without a performance or infrastructure cost, and it works in both React and Vue. In practice: we can commit to media features without new infrastructure, we get a storage-savings metric for free, we cut wasted admin API calls, and upgrading an existing media library is a single command rather than a migration project.
To engineering leadership: v1.5.0 (unchanged, still the architectural anchor) adds a queueable MediaUploaded event dispatched from the single upload service that all entry points funnel through, so downstream packages subscribe with ShouldQueue listeners and stay off the request path; it persists optimization pipeline state on the media row (status, timestamp, bytes saved, original size, formats, error) and exposes a stable optimization block on MediaResource with matching TypeScript types, plus media:backfill-optimization-status to reconcile legacy rows. v1.6.0 is a frontend release with no breaking changes: useMediaPicker (React and Vue) gains an enabled option that defers initial media and recently-used fetches and then loads once — the Vue composable accepts a ref or getter — and MediaModal forwards its open state so a mounted-but-closed modal makes no /api/media requests; the list loads on first open, close/reopen does not refetch unchanged filters, and page changes or refresh() calls made while closed are applied on next open. It also fixes useMediaLibrary honouring autoFetch: false under React StrictMode re-runs and the Vue composable swallowing the first filter change when autoFetch is false. Separately, the release workflow now queues docs and changelog imports on the ArtisanPack UI docs site, skipped when DOCS_API_TOKEN is unset — worth noting for whoever owns the release pipeline, as it is a new external dependency in CI. Net risks remain: schema surface area, the operational requirement for queue workers and the backfill during rollout, and a fetch-on-open model that will read as "empty modal" if a consumer assumes eager loading.
To product/design: Editors get accurate optimization status in the media library UI (including a null-safe shape for non-image assets), pointer and keyboard affordance fixes, and — new in v1.6.0 — a picker that opens instantly with fresh data instead of pre-loading in the background, and that preserves filters and pagination across close/reopen so an editor does not lose their place. A status-badge and "storage saved" surface is now a small frontend task rather than a backend project, and adding the picker to a new screen is a cheap embed rather than an API-cost conversation — both worth slotting next to any existing media-management roadmap item.
How can I help you explore Laravel packages today?