saade/fluppy
Fluppy is a lightweight Laravel package for handling file uploads with a fluent, chainable API. It simplifies validation, storage, and cleanup while staying out of your way.
Requirements: PHP 8.2+, Filament 4 or 5. Install with composer require saade/fluppy:"^1.0".
This is not a zero-config install. Fluppy ships CSS that must be compiled through your own Filament panel theme. If you don't already have a custom theme (php artisan make:filament-theme), create one, then add to resources/css/filament/{panel}/theme.css:
@import '../../../../vendor/saade/fluppy/resources/css/fluppy.css';
@source '../../../../vendor/saade/fluppy/resources/views/**/*.blade.php';
and rebuild assets (npm run build / vite build). Skipping this is the most common "it looks broken" first-hour problem.
First use case: swap FileUpload::make('attachments') for FluppyField::make('attachments') and keep the rest of your chain (->disk(), ->directory(), ->multiple(), validation rules). Test with a small file first, then one larger than upload_max_filesize to confirm chunking actually kicks in.
Before letting anyone upload big files, run php artisan fluppy:doctor in staging. It audits the full path a large upload travels: Livewire, PHP ini, web server config, and bucket CORS. Re-run it after any infra change.
For S3, set Livewire's temporary upload disk to S3 so large files become multipart uploads straight to the bucket, and configure bucket CORS accordingly.
Read the README's per-source setup section before enabling anything beyond the local disk — every third-party source (Drive, Dropbox, Zoom, etc.) needs your own OAuth credentials.
FileUpload: image(), acceptedFileTypes(), maxSize(), imageEditor(), reorderable(), openable(), downloadable(), custom disk()/directory(). Fluppy layers chunked uploads and extra sources on top rather than replacing the API.multiple() inside a repeater, test each row independently and cap files with maxFiles() so one row can't saturate connections.SpatieMediaLibraryFluppyField (a separate class) instead of the base field in Spatie Media Library resources; keep your collectionName() and conversions. Conversions run after the upload finishes, so move heavy image/video conversions to a queue rather than sync.wire:poll or try to drive your own progress bar from a wire:model property — upload state lives in Uppy, not in a normal Livewire attribute.maxSize is in the hundreds of MB or uploads are frequent; keep a local temp disk for local development so you don't need a bucket to work offline.php artisan fluppy:prune daily (in routes/console.php on Laravel 11+) to remove unfinished-upload leftovers and aborted multipart parts.@source line is Tailwind 4 syntax. If your Filament theme is still on a Tailwind 3 pipeline, confirm the README's instructions apply — otherwise vendor Blade classes are silently not scanned and the field renders unstyled.composer what-not saade/fluppy (or composer why-not) to see the conflicting package when the constraint fails.upload_max_filesize, post_max_size, max_execution_time, max_input_time, nginx client_max_body_size, proxy read timeouts, plus any CDN/WAF body-size cap. fluppy:doctor is the intended debugger here — start there rather than chasing a bare 413.PUT/POST from your app origin with the right exposed headers; a bad rule looks identical to "upload just doesn't happen". Add bucket lifecycle rules for aborted multipart uploads as a backstop to fluppy:prune.post_max_size and client_max_body_size.maxSize() and acceptedFileTypes() up front to avoid wasted bandwidth, and don't skip fluppy:prune.FileUpload with FluppyField still leaves you on the base field. Use SpatieMediaLibraryFluppyField."^1.0" since this is a first release.How can I help you explore Laravel packages today?