Uploads
Upload routes are opt-in. Register them in the host Laravel application's route file before using package-managed temporary, chunked, or direct uploads.
use Illuminate\Support\Facades\Route;
Route::inertiaFormUploads();
The defaults use the /_inertia-forms prefix, inertia-forms.* route names, and web plus auth middleware. The macro accepts custom prefix, middleware, and name arguments.
Choose a strategy
use Inertify\Form\Fields\File;
File::make('document')->storeWithForm();
File::make('avatar')->image()->maxSize(5 * 1024);
File::make('archive')->chunked(5 * 1024 * 1024);
File::make('video')->directToStorage('s3');
| Strategy | Behavior |
|---|---|
storeWithForm() | Sends native browser files with the final Inertia request. |
| Temporary (default) | Uploads before submission and stores an encrypted, expiring token in form data. |
chunked(bytes) | Uploads resumable chunks and can continue from the server-reported offset. |
directToStorage(disk) | Uses a disk's temporary upload support or multipart API; non-S3 disks use the package fallback. |
Configure upload content with image(), accept(), minSize(), maxSize(), dimensions(), minFiles(), and maxFiles(). These constraints validate actual upload content, not token strings. requireValidatedUploads() and validateUploadsUsing() support reusable validation profiles.
The serialized field contains both the documented flat route properties and an additive upload descriptor. You can override endpoints with temporaryUploadUrl(), temporaryUploadDeleteUrl(), or a custom upload() descriptor when an application-owned transport is required.
Render upload state
Use useFile() in an application renderer. It composes the field controller with upload progress and mutation methods.
<script setup lang="ts">
import { useFile, type FormFieldSlotProps } from '@inertify/form-vue'
const props = defineProps<FormFieldSlotProps>()
const file = useFile(props.name, props.form)
function select(event: Event) {
const input = event.currentTarget as HTMLInputElement
if (input.files) {
void file.upload(input.files)
}
}
</script>
<template>
<div v-if="visible">
<label :for="`file-${name}`">{{ field.label }}</label>
<input
:id="`file-${name}`"
type="file"
:multiple="field.multiple === true"
:accept="Array.isArray(field.accept) ? field.accept.join(',') : field.accept"
:disabled="disabled"
@change="select"
>
<progress
v-if="file.uploadState.progress !== null"
:value="file.uploadState.progress"
max="100"
/>
<ul>
<li v-for="(uploaded, index) in file.uploadState.files" :key="uploaded.key">
{{ uploaded.name }}
<button type="button" @click="file.remove(index)">Remove</button>
</li>
</ul>
</div>
</template>
useFormUploads() manages all upload paths and exposes upload, retry, pause, resume, remove, reorder, cancel, and clear. FormUploads exposes the same behavior through a renderless slot. Pause and resume are meaningful for resumable strategies; the state always reports status, progress, bytes, errors, uploaded resources, and pending browser files.
Resolve submitted uploads
After validation, keep normal model attributes separate from token-based files:
$data = $form->validated(files: false);
if ($avatar = $form->upload('avatar')) {
$data['avatar_path'] = $avatar->store('avatars', 'public');
}
foreach ($form->uploads('gallery') as $upload) {
$upload->store('gallery', 'public');
}
$profile->update($data);
upload() returns one SubmittedUpload or null; uploads() follows the submitted order. Tokens are encrypted and expiring, and the server rejects tampered, expired, disk-mismatched, or validation-profile-mismatched values. Existing files resolve separately and cannot be stored as new uploads.
For storeWithForm(), read the native file from validated() or Laravel's request file bag instead. Request macros formUpload('avatar') and orderedFormUploads('gallery') are available when direct request access is more convenient.
Existing files and Media Library
Serialize retained files with ExistingFile::fromDisk() or the optional Spatie Media Library adapter:
use Inertify\Form\Uploads\ExistingFile;
File::make('gallery')
->multiple()
->mediaCollection('gallery')
->existingFiles(
fn () => ExistingFile::fromMediaLibrary($profile->getMedia('gallery')),
);
After validation, MediaLibraryUploads::syncCollection() can synchronize retained, new, removed, and reordered items. The adapter does not install or configure Media Library and throws a clear exception for incompatible models.
Clean expired uploads
Run cleanup manually or schedule it with Laravel:
php artisan form:cleanup-uploads
php artisan form:cleanup-uploads --lifetime=7200
use Illuminate\Support\Facades\Schedule;
Schedule::command('form:cleanup-uploads')->hourly();
Cleanup scans only package-owned directories, aborts expired pending S3 multipart sessions, retains completed direct uploads for the submission-token lifetime, and leaves individual failures retriable.