codebyray/livewire-media-uploader
A reusable Livewire v3/v4 component for Spatie Media Library with publishable Tailwind or Bootstrap views, drag-and-drop uploads, inline caption/description/order editing, reordering, conflict strategies, SHA-256 duplicate detection, and optional authorization hooks.
Architecture fit
This package is a UI-layer adapter, not a platform. It wraps Spatie Media Library in a reusable Livewire component and ships a publishable Blade view, which means its architectural fit is entirely determined by two pre-existing decisions: (1) is the app a Laravel + Livewire monolith, and (2) is Spatie Media Library already the media abstraction of record?
HasMedia models, configured disks, and conversions. In that case this package eliminates a recurring piece of bespoke work — drag-and-drop, progress, metadata editing, reorder, delete confirmation — that otherwise gets rebuilt (inconsistently) on every model that needs uploads.v0.5.0 on security grounds (Composer audit blocks fresh installs on those lines). Being stuck on v0.4.x means immediately inheriting an unsupported branch — that is a disqualifying condition, not a trade-off.spatie/laravel-medialibrary, its migrations, disk conventions, conversion pipeline, and queue configuration. That is a substantial platform decision being made implicitly by a 7-star UI component.The correct mental model for the TPM: we are not evaluating a component, we are evaluating whether to standardize the upload surface of the application on Spatie's data model with a thin, third-party Livewire skin on top.
Integration feasibility
High on the happy path, with a small number of sharp edges that only surface in production.
composer require with auto-discovery; two component aliases are registered (media-uploader, media.media-uploader), so both naming conventions work.multiple, showList, accept, allowedTypes, allowedMimes, maxSizeKb, onNameConflict, skipExactDuplicates, authorizeAbility.media-uploader-config, media-uploader-views), which is the right escape hatch for a package this young.pendingPostId) is the single most complex integration path and the one most likely to need a spike. It introduces a pending-media state machine the parent component must own.Feasibility gaps to verify before committing:
order_column is native Spatie; caption/description are almost certainly custom_properties or added columns. We need to know which, because a schema change on the media table is a different risk class than a JSON blob.dark strategy, so an app on Tailwind 4 or on a non-standard dark-mode config will need view edits on day one.Technical risk
Rated medium-high, concentrated almost entirely in supply chain and versioning rather than in code quality.
0.x, minor bumps may carry breaking changes. Every composer update is a potential deploy-time break with no deprecation window.spatie/laravel-medialibrary ^10.12|^11 ceiling means a Spatie v12 release blocks us until the author updates, and Spatie is the dependency we actually care about.authorizeAbility hook delegates to our Gate/Policy, which is the right design (no bundled auth package), but the default posture must be confirmed. An uploader mounted without ability wiring is an unauthenticated write path to our media collections.videos preset allows 102,400 KB. Livewire's temporary upload rules, upload_max_filesize, post_max_size, Nginx client_max_body_size, and PHP-FPM timeouts all sit between the browser and Spatie. Files traverse the web tier twice (browser → PHP temp → storage) rather than going direct-to-S3. This is a known, architectural limitation of Livewire uploads, not a defect in this package — but the package's generous default preset will hit it.Key questions
v0.5.x line or the EOL-adjacent v0.4.x pin?HasMedia models and conversions? If not, is this package the trigger for adopting it?custom_properties or media table columns? Does adoption require a migration?authorizeAbility fail closed or fail open when omitted? Can we demonstrate a denied upload?Build-vs-buy signal: the component surface is small. If the answer to Q1 or Q2 is unfavorable, or if the maintainer goes quiet, vendoring the component into our own namespace is a low-cost exit rather than a rewrite. That exit option materially de-risks adoption and should be stated up front as the contingency.
Stack fit
Adopt only if the following all hold: Laravel 12 or 13, PHP 8.2+, Livewire 3 or 4, Spatie Media Library already present, and a Blade-rendered front end with Tailwind (or Bootstrap, if that is our design system). Any one of these failing downgrades the assessment from "adopt" to "reference implementation" — read the source, copy the patterns, do not take the dependency.
Where it fits, the value is standardization: one uploader UX across every model with media, one config file for presets and limits, one place to fix an accessibility bug, instead of N bespoke implementations.
Migration path
Three distinct paths, in ascending risk:
singleFile() semantics match, whether existing order_column values survive reordering. Confirm that our caption/description storage matches what the component reads and writes before swapping any UI.pendingPostId-style handoff. Build this last, behind the pilot, and only if the create-with-attachments UX is a genuine product requirement — it is the most failure-prone path and the least exercised by the package's examples.The migration that must not be overlooked: there is no automated path back. Once uploads flow through this component, the published Blade view becomes part of our codebase. Reverting means restoring our prior UI, not just removing the dependency.
Compatibility
Pin to a narrow range (0.5.*) and treat every minor bump as a potential breaking change requiring review. Add the package to dependabot/renovate but explicitly disable auto-merge. Verify the Spatie constraint (^10.12|^11) against our installed version before install — a v12 in the lockfile makes the package uninstallable.
Confirm in a spike, not in a document: Tailwind version compatibility, dark-mode class strategy against our layout, Alpine availability under our exact Livewire version, and the behavior of the two component aliases under Livewire 4's component resolution.
Sequencing
singleFile + multiple combination, a denied authorizeAbility call, keyboard-only upload and delete, and the page with Alpine deliberately broken (to see the degradation mode). This spike answers most of the key questions above.App\Livewire and move on. Budget: small.Maintenance
Recurring, modest, and non-zero — this is not a fire-and-forget dependency.
MEDIA_* environment variables that map into config/media-uploader.php. Every environment change requires php artisan config:clear, which must be in the deploy script. Config cached in production and stale env values is a classic silent-failure pairing here.Support
There is effectively no external support channel. 7 stars and zero dependents means questions go unanswered publicly and there is no community knowledge base to search. Internal ownership is the only realistic model: a named maintainer, a runbook, and a documented list of mount points (which model, which collection, which policy, which preset) so that an on-call engineer can diagnose an upload failure without reading the package source.
The one thing that helps: the GitHub Actions test matrix across PHP/Laravel/Livewire combinations means the supported matrix is empirically verified, so "does it work on our version combination" is answerable by reading CI rather than by trial.
Scaling
multiple uploads multiply this. Concurrency planning should be done against worker count, not against request rate.nonQueued() conversions. In production, queued conversions are usually correct, but then the UI shows originals until the queue drains — a UX and support consequence that must be decided deliberately, not inherited from the example.Failure modes
Ranked by likelihood × user impact:
maxSizeKb disagrees with Livewire temp-upload rules, upload_max_filesize, or Nginx limits. User sees a generic or silent failure. Mitigation: align all four, document the effective ceiling per preset in the UI.singleFile() + multiple silent data loss. Documented by the author, but a genuine footgun: every file processes, only the last survives, and the user believes they uploaded a set. Mitigation: never set multiple on a single-file collection; enforce in code review and a feature test.Ramp-up
Low for a Laravel-fluent team: half a day to a working upload in a dev environment, two to three days to production-hardened (limits, policy, theming polish, i18n, accessibility pass). The README is above average for a package this size — copy-paste examples for every targeting mode, a clear warning about singleFile(), and an honest note about why Laravel 10/11 support was dropped, which reads as a maintainer who understands that security posture is a feature.
The non-trivial ramp is not the package — it is Spatie Media Library itself. Disk strategy, conversions, responsive images, queued processing, and custom_properties are where the learning curve actually lives. Teams already running Spatie will onboard this in an afternoon. Teams adopting Spatie because of this package should treat the Spatie evaluation as the primary decision and the uploader as a downstream convenience.
Bottom line. Adopt on the supported matrix with a narrow pin and published views, pilot on a greenfield collection, and keep vendoring as a funded contingency. Decline — or copy the patterns without the dependency — if we are outside the supported Laravel/Livewire/PHP matrix, if Spatie is not already the media layer, or if large-file/video handling is a core requirement rather than an edge case.
How can I help you explore Laravel packages today?