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

Laravel Filepond Laravel Package

rahulhaque/laravel-filepond

Straightforward backend for Laravel apps using the FilePond upload library. It manages temporary storage, validation and cleanup, supports single/multiple, chunked and native S3 multipart uploads with resume, covers process, patch, head, revert and restore endpoints, and works with Spatie medialibra...

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish in four commands

composer require rahulhaque/laravel-filepond:"^13.0"   # match your Laravel major
php artisan vendor:publish --provider="RahulHaque\Filepond\FilepondServiceProvider"
php artisan migrate
php artisan vendor:publish --tag=filepond-config   # if config isn't included above

Note the version pinning convention: the package tracks Laravel majors (^10.0, ^11.0, ^12.0, ^13.0), and the README for each major lives on a matching branch. If you're on Laravel 10 do not install ^13.0 — Composer won't stop you if constraints are loose, but the service provider and migration differ. When upgrading across majors (12.x → 13.x), delete the old published migration before republishing.

Where to look first

  1. config/filepond.php — the whole package is really configured here. Read disk, temp_disk, temp_folder, expiration, validation_rules, middleware, soft_delete before writing any code.
  2. routes/ inside the package (or php artisan route:list | grep filepond) — you need to know that process, patch, head, revert, and restore endpoints exist and how they're namespaced.
  3. The filepond migration — it creates the fileponds table that tracks every temp upload. Understand this table; it's the source of truth for moveTo() and the cleanup command.

First use case: avatar upload

Frontend — point FilePond at the package's server URL and pass CSRF:

FilePond.setOptions({
  server: {
    url: "{{ config('filepond.server.url') }}",
    headers: { 'X-CSRF-TOKEN': "{{ csrf_token() }}" },
  },
});
FilePond.create(document.querySelector('input[name="avatar"]'));

Backend — validate with the package's rule, then move:

$this->validate($request, [
    'avatar' => ['required', Rule::filepond(['image', 'max:2000'])],
]);

$fileInfo = Filepond::field($request->avatar)->moveTo('avatars/avatar-' . auth()->id());

That's the entire happy path. The file lands in temp storage the moment the user drops it, Rule::filepond() validates the temp file, and moveTo() copies it to the configured permanent disk, deletes the temp copy, and returns id, dirname, basename, extension, mimetype, filename, location, url. Store location in your model; don't recompute paths.

One non-negotiable: if you use Laravel Debugbar, add filepond* to the except array in config/debugbar.php. The upload endpoints return raw JSON and Debugbar's injected payload corrupts FilePond's parser. This is the single most common "it doesn't work" report.

Implementation Patterns

Always gate on the field name

Filepond::field($value) is mandatory and is the anchor for every downstream call. The second parameter $checkOwnership matters more than it looks: it scopes the lookup to the current session/user so one user can't submit another user's temp file ID and steal the upload. Enable it for any authenticated flow. It also covers the guest-then-login case — a file uploaded before login can be claimed after authentication.

Single vs. multiple

Same API, different shape of return value. $request->avatar (string) returns one info array; $request->gallery (array, from name="gallery[]") returns an array. Multiple files get trailing incrementals appended automatically (gallery-1-1.png, gallery-1-2.png), so you pass a base filename, not a full filename.

$files = Filepond::field($request->gallery)->moveTo('galleries/gallery-' . auth()->id());
foreach ($files as $file) {
    $user->photos()->create(['path' => $file['location']]);
}

Validate multiples with 'gallery.*' => ['required', Rule::filepond([...])] — the wildcard is required, otherwise a mixed bag of valid and invalid files slips through.

moveTo vs. copyTo vs. delete

  • moveTo($path, $disk = null) — copy + delete temp. Use this 90% of the time.
  • copyTo($path, $disk = null) — copy, keep the temp file alive. Use when the same upload feeds multiple destinations (e.g., original + thumbnail pipeline where you generate the derivative from the temp file).
  • delete() — remove the temp file without persisting anything. This is your rollback path when a later step in the request fails.

The optional second argument overrides the config disk per call, which is how you handle "avatars go to S3, documents go to the local NAS" without juggling multiple configs.

Wrap persistence in a transaction, defer the move

moveTo() physically touches storage, which doesn't roll back. The reliable pattern is: validate → create the DB record with a placeholder → moveTo() → update the record inside the same DB transaction, and if the transaction throws, call Filepond::field(...)->delete() in the catch block so you don't leak temp files.

Chunked and large uploads

For video or large PDFs, enable chunkUploads: true on the FilePond instance. The package handles patch for chunk assembly and head for resume, and it supports native S3 multipart when temp_disk is S3. Chunking is a client-side flag plus config; there is no backend code change. Do not attempt to Rule::filepond() validate when temp storage is remote — see Gotchas.

Spatie laravel-medialibrary

The package is explicitly compatible. Since moveTo() returns location and mimetype, the integration is: moveTo() into a staging path, then $model->addMedia(...)->toMediaCollection(), or let medialibrary own the file and use copyTo(). Either way, keep medialibrary's disk aligned with filepond.disk to avoid a cross-disk copy.

File metadata plugin

If you use filepond-plugin-file-metadata, the metadata arrives in the request alongside the file and is stored on the Filepond model record. Read it before calling moveTo() if you want to derive folder names from user-supplied tags — but treat it as untrusted input and sanitize before it reaches a path.

Cleanup as a scheduled job

// routes/console.php or Kernel::schedule()
$schedule->command('filepond:clear')->hourly();

Default expiration is 30 minutes. For a form that users routinely leave open, that's aggressive — bump to 120–240 minutes rather than disabling cleanup. Run filepond:clear --all manually only as a reset; it truncates the fileponds table and wipes the temp directory regardless of expiry.

Gotchas and Tips

Third-party temp storage kills Rule::filepond(). If temp_disk points at S3, files upload straight to the cloud and are never on your app server. Rule::filepond() will throw "file not found," and so will any code that inspects the temp file (dimensions, MIME sniffing). You're left with the global validation_rules in config, which only apply at the /process endpoint. Decide early: local temp = rich validation, remote temp = scale and direct-to-cloud. You cannot have both.

soft_delete is on by default — mind your disk. With soft_delete = true, moveTo() marks the Filepond row deleted rather than removing it, preserving an audit trail of every upload. That's good for compliance, bad if you're storing PII and assume it's gone. Set it to false for GDPR-sensitive flows, or add your own pruning job on the fileponds table.

Validation rule ordering. Put Rule::filepond([...]) in the same array as required, and remember the rules inside it are applied to the temp file, not the request value. A common mistake is writing 'avatar' => ['required', 'image', 'max:2000'] — plain image/max rules run against the hidden input string and either fail silently or pass trivially. Everything file-related goes inside Rule::filepond().

Permission failures masquerade as logic bugs. If files survive cleanup or moveTo() silently no-ops on a fresh deploy, it's almost always the temp directory's ownership/permissions (or the S3 bucket policy). Fix permissions, then php artisan filepond:clear --all for a clean slate before debugging anything else.

Middleware defaults are web + auth. That's wrong for public forms (contact, job application). Change middleware in config, and remember that unauthenticated users then need field($value, false) or the ownership check will reject their own uploads at submission time.

Don't hardcode the server URL. Use config('filepond.server.url') in your Blade/Inertia payload. It resolves route names, so it survives route prefix changes and localization middleware — hardcoded /filepond/process will break the moment you add a locale segment.

Check head/restore before blaming chunking. When resume misbehaves, hit the endpoints manually (HEAD /filepond/head/{id}) and inspect the fileponds table. The package's own bookkeeping — not the browser — is usually what's stale, and --all clears it.

Extension is automatic; don't append it yourself. moveTo('avatars/avatar-1') produces avatar-1.png. Passing moveTo('avatars/avatar-1.png') yields avatar-1.png.png. Pass the basename only.

Route model binding is absent by design. You never touch the Filepond model directly in normal use; the facade resolves it from the request value. If you find yourself querying Filepond::where(...), you've probably missed field() and are rebuilding logic the package already owns.

Debugging order of operations: (1) Debugbar excluded? (2) Does the fileponds row exist after drop? (3) Does the temp file exist on disk? (4) Does Rule::filepond() pass in isolation? (5) Does moveTo() return an info array? That sequence isolates frontend, storage, validation, and persistence in five cheap steps.

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.
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc