axn/livewire-upload-handler
Modern file upload handler for Livewire 3 with chunked uploads, Glide image previews, drag and drop, sortable files, validation, themes, and Spatie Media Library integration. PHP 8.4 and Laravel 12.
Architecture fit. This is a Livewire-native component library, not a service or SDK. It ships two parallel hierarchies — Group/Item for storage you implement yourself, and MediaGroup/MediaItem for Spatie Media Library auto-save — with an explicit extension contract (itemComponentClassName(), initialEntities(), saveUploadedFile(), deleteSavedFile(), savedFileName()) plus php artisan make:upload-handler scaffolding. For a Blade-rendered Livewire monolith this is a clean fit: components drop into existing forms, state stays inside Livewire, and persistence is a subclass point rather than a framework modification. It is a poor fit for Inertia/Vue/React front ends — you would be paying a Livewire runtime tax to render a widget that FilePond or Uppy handles without the coupling.
Integration feasibility. Mechanically easy, contractually constrained. Install is composer require, two Blade stacks (@livewireUploadHandlerStyles / @livewireUploadHandlerScripts), optional config and theme publishing, and two pieces of housekeeping that are easy to miss and painful to discover late: livewire-tmp/ in storage/app/.gitignore, and a self-ignoring Glide cache directory at storage/app/.livewire-upload-handler-glide-cache. Note this is runtime state inside storage/app — that has deployment and backup implications covered below.
The hard gate is the platform floor: PHP 8.4+ (asymmetric visibility, enums, typed exceptions), Laravel 12+, Livewire 3.1+. That is not a preference, it is an estate-wide upgrade prerequisite. If any service sharing this composer lock must remain on PHP 8.3, adopting the package becomes an infrastructure program rather than a dependency addition.
Technical risk. This is the decisive section. A 1-star, 0-dependent repository with a single corporate maintainer (AXN Informatique) has no ecosystem validation, no Stack Overflow corpus, no third-party security review, and no contractual continuity. The release history shows breaking changes landing on minor and patch versions: 1.3.0 renamed all four public events (livewire-upload-handler:* → luh-*), 1.2.0 changed the extension API and flipped savedFileDisk to null by default, 1.1.0 renamed saveItemOrder → saveFileOrder. That cadence is acceptable for a pinned internal dependency and disqualifying for anything you intend to upgrade casually.
Secondary risks: Glide coupling forces a second image pipeline if you already run Cloudinary, imgix, or Intervention; MIME and dimension validation (acceptsMimeTypes, minWidth/maxHeight) is partly client-side and must be re-enforced server-side before you treat it as a control; and the package provides nothing for AV scanning, quarantine, retention, or transcoding — those are yours to schedule.
Key questions to resolve before commit
chunk_size (auto-derived from upload_max_filesize) survive our load balancer, reverse proxy, and PHP-FPM body limits end to end?Stack fit. Adopt only where the target is Laravel 12+/Livewire 3.1+ on PHP 8.4+, and only where at least two of the following hold: chunked large-file upload, image previews, drag-and-drop, sortable ordering, Spatie Media Library persistence. A single small avatar field does not justify the dependency surface. Treat the Media Library path as the default when media() relationships already exist; treat the generic Item path as the escape hatch when files must land in a bespoke table or non-Spatie storage.
Migration path. Do not big-bang. Sequence a three-step ramp:
<livewire:upload-handler.item> with acceptsMimeTypes, maxFileSize, and showImagePreview, no autoSave, no Media Library. This isolates the asset pipeline, CSP and Blade directive ordering, and gitignore hygiene. Exit criterion: upload, preview, cancel, and re-upload all work in staging with production-like file sizes.<livewire:upload-handler.media-group> against an existing model and collection with autoSave, sortable, and maxFilesNumber set. This is where Spatie config, conversions, and disk assumptions get exercised. Exit criterion: order persists across reload, deletion removes both media rows and Glide cache, and luh-media-saved fires with a usable mediaId.Item/Group for bespoke storage or scaffold via make:upload-handler. Standardize theming (bootstrap-5 + fontawesome-7 defaults, or publish and override) at this stage, not earlier — theme overrides are a drift surface against upstream Blade changes.Compatibility. Pin the exact version in composer.json and treat every bump as a change request requiring a read of UPGRADE.md and the release notes. Maintain a compatibility matrix in the repo README covering PHP minor, Laravel minor, Livewire minor, and package version — the floor moves (1.5.0 added Laravel 13 support; 1.5.2 added axn/laravel-glide 4 support). Watch transitive constraining: the Glide dependency and the PHP 8.4 requirement both narrow what else can live in the lock file.
Sequencing against the roadmap. Front-load the PHP 8.4 platform upgrade as its own milestone with its own rollback plan — never bundle it with the feature that motivated the package. Then run the pilot behind a feature flag in a non-critical flow. The chunked-upload capability (video, high-resolution imagery, scanned PDFs) is the actual roadmap unlock; schedule the feature that needs chunking immediately after the pilot, not the general media library rollout, so value lands before risk accumulates.
Maintenance. Ongoing cost is modest but non-zero and cannot be fully delegated: version-pin discipline, UPGRADE.md review on every bump, theme/view override drift, and a standing fork contingency. The fork is cheap in theory (MIT, small codebase) and expensive the first time — budget 2–5 engineer-days to establish a vendored path, patch workflow, and CI that runs against it. Log the single maintainer as a named risk-register entry with quarterly review, not a one-time note.
Support. Self-support only. No SLA, no CVE process, no LTS policy, no paid tier. Escalation is "read the source," which is feasible here — the extension seams are documented and the code is small — but it means the adopting team must contain at least one engineer comfortable with Livewire internals. Do not adopt where the only path to a fix is filing an issue.
Scaling. Four pressure points:
media rows — watch contention on large galleries.maxFilesNumber=50 triggers 50 derivative generations, so warm or cap the cache before exposing it to heavy traffic.Failure modes to instrument. Orphaned livewire-tmp chunks after aborted uploads (needs a scheduled prune). Stale Glide cache after delete or replace (needs an invalidation hook on luh-media-deleted). Divergence between Spatie media rows and underlying files if autoSave partially fails mid-batch. Silent validation gaps where acceptsMimeTypes and dimension bounds were enforced only in the browser. Hard breaks on package upgrades that changed event names — any Alpine x-on listener is a rename casualty. And lost-in-progress uploads when a deploy restarts nodes mid-chunk.
Ramp-up. Low-to-moderate. The docs set is genuinely good (installation, configuration, basic usage, media library, customization, advanced usage, events, troubleshooting, plus an UPGRADE.md and an agent skill file), so a Livewire-fluent engineer is productive in under a day. Budget instead for the organizational ramp: getting PHP 8.4 approved and rolled out, agreeing the pin-and-review policy, and naming the fork owner. Those are the tasks that actually gate the timeline.
How can I help you explore Laravel packages today?