Configuration and commands
The service provider is discovered by Laravel. It always merges package configuration and registers macros and commands, but it never registers upload routes automatically.
Publish configuration
Publish config/inertia-forms.php only when the defaults need to change:
php artisan vendor:publish --tag=inertia-forms-config
The aliases inertify-form-config and inertify-form publish the same file.
Configuration reference
All keys live below inertia-forms.
| Key | Default | Unit | Purpose |
|---|---|---|---|
file_uploads.route_prefix | /_inertia-forms | — | URL prefix used when upload routes are registered. |
file_uploads.route_name | inertia-forms. | — | Route-name prefix. A trailing dot is normalized automatically. |
file_uploads.middleware | ['web', 'auth'] | — | Middleware applied to the complete upload route group. |
file_uploads.temporary_uploads.disk | '' | — | Laravel filesystem disk for temporary and chunked content. Empty uses an isolated local directory at storage/inertia-forms-temporary-uploads. |
file_uploads.temporary_uploads.lifetime | 3600 | seconds | Token lifetime and the normal temporary cleanup age. |
file_uploads.temporary_uploads.max_size | 10240 | KiB | Maximum normal temporary upload size before field-specific constraints. |
file_uploads.temporary_uploads.chunked.size | 5242880 | bytes | Default client chunk size. |
file_uploads.temporary_uploads.chunked.max_size | 2097152 | KiB | Maximum completed chunked upload size. |
file_uploads.direct_to_storage.disk | '' | — | Default filesystem disk for direct uploads. Empty requires a disk on each direct File field. |
file_uploads.direct_to_storage.url_lifetime | 900 | seconds | Lifetime for a direct-upload session or signed upload URL. |
file_uploads.direct_to_storage.part_size | 16777216 | bytes | Multipart part size. S3 requires every non-final part to be at least 5 MiB. |
file_uploads.direct_to_storage.multipart_threshold | 104857600 | bytes | Files larger than this use multipart direct upload. |
file_uploads.direct_to_storage.max_size | 5242880 | KiB | Maximum direct upload size before field-specific constraints. |
authorization.throw_on_unauthorized | false | — | Throw during serialization instead of returning an empty form resource. Validation always throws for an unauthorized form. |
Field methods such as maxSize(), chunked($bytes), directToStorage($disk), partSize(), and multipartThreshold() override the corresponding serialized transport settings for that field.
Example production configuration
return [
'file_uploads' => [
'route_prefix' => '/_inertia-forms',
'route_name' => 'inertia-forms.',
'middleware' => ['web', 'auth', 'throttle:uploads'],
'temporary_uploads' => [
'disk' => 'private',
'lifetime' => 3600,
'max_size' => 10 * 1024,
'chunked' => [
'size' => 5 * 1024 * 1024,
'max_size' => 2 * 1024 * 1024,
],
],
'direct_to_storage' => [
'disk' => 's3',
'url_lifetime' => 900,
'part_size' => 16 * 1024 * 1024,
'multipart_threshold' => 100 * 1024 * 1024,
'max_size' => 5 * 1024 * 1024,
],
],
'authorization' => [
'throw_on_unauthorized' => true,
],
];
Choose disks and limits for the host application. The example values are not a substitute for application-specific storage, cost, retention, or abuse controls.
Upload routes
Register the endpoints explicitly in an application route file:
use Illuminate\Support\Facades\Route;
Route::inertiaFormUploads();
Override the group settings with named arguments:
Route::inertiaFormUploads(
prefix: '/form-assets',
middleware: ['web', 'auth', 'verified', 'throttle:uploads'],
name: 'form-assets',
);
For a route-name override, point fields at the same name prefix:
File::make('avatar')->uploadRoutes('form-assets');
Registered endpoints
Paths below are relative to the configured prefix. Names below are relative to the configured name prefix.
| Verb | Path | Route name suffix | Purpose |
|---|---|---|---|
POST | file-upload | file-upload.store | Store a temporary upload. |
DELETE | file-upload | file-upload.destroy | Remove an unused upload token. |
POST | file-upload/chunked/start | file-upload.chunked.start | Create a resumable chunk session. |
GET | file-upload/chunked/status | file-upload.chunked.status | Read the accepted byte offset. |
POST | file-upload/chunked/chunk | file-upload.chunked.append | Append the next chunk. |
POST | file-upload/chunked/complete | file-upload.chunked.complete | Validate and finalize chunked content. |
DELETE | file-upload/chunked/abort | file-upload.chunked.abort | Abort a chunk session. |
POST | file-upload/direct/start | file-upload.direct.start | Create a direct or multipart session. |
PUT | file-upload/direct/object | file-upload.direct.object | Local-disk direct-upload fallback. |
POST | file-upload/direct/part | file-upload.direct.part | Sign an S3 multipart part or upload a local fallback part. |
GET | file-upload/direct/status | file-upload.direct.status | Read direct-upload progress. |
POST | file-upload/direct/complete | file-upload.direct.complete | Finalize direct content and return a submission token. |
DELETE | file-upload/direct/abort | file-upload.direct.abort | Abort a direct session. |
storeWithForm() does not need package upload routes. A complete custom upload descriptor can also use application-owned endpoints.
Route security
The package encrypts and expires upload tokens, restricts direct uploads to the signed disk profile, validates declared sizes, and rejects malformed, expired, purpose-mismatched, disk-mismatched, and rule-profile-mismatched tokens. These protections complement rather than replace application access control.
File methods such as image(), accept(), minSize(), maxSize(), and dimensions() become an encrypted validation profile for package-managed uploads. To require a profile even without those constraints, use:
File::make('document')->requireValidatedUploads();
Add application-specific content checks with a container-resolved validator class:
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Inertify\Form\Contracts\ValidatesFileUploads;
use Inertify\Form\Uploads\TemporaryUpload;
use Inertify\Form\Uploads\UploadRules;
final class ScanDocumentUpload implements ValidatesFileUploads
{
public function validate(
TemporaryUpload $upload,
UploadedFile $file,
UploadRules $rules,
Request $request,
): void {
// Run the application's malware/content policy or throw.
}
}
File::make('document')->validateUploadsUsing(ScanDocumentUpload::class);
Artisan commands
Generate a form
php artisan make:form ProfileForm
The generator creates App\Forms\ProfileForm by default with an empty fields() method.
Clean expired uploads
php artisan form:cleanup-uploads
Override the normal temporary lifetime for one run:
php artisan form:cleanup-uploads --lifetime=7200
--lifetime is a non-negative number of seconds. Cleanup:
- scans only package-owned upload directories on the configured temporary and direct disks;
- deletes expired temporary and chunked content;
- aborts expired pending S3 multipart sessions before deleting their metadata;
- retains completed direct uploads for the submission-token lifetime;
- continues after individual failures and returns a failing exit code when any operation remains retriable.
Schedule it in the host application:
use Illuminate\Support\Facades\Schedule;
Schedule::command('form:cleanup-uploads')->hourly();
The cleanup command is operational maintenance, not an upload authorization control. Run it often enough for the selected storage cost and retention policy, and monitor non-zero exits.
Custom transports
File::upload(array|Closure $descriptor) replaces the nested headless upload descriptor. temporaryUploadUrl() and temporaryUploadDeleteUrl() override the normal temporary endpoints. When using application endpoints:
- return the token/file payload expected by the Vue upload transport or provide a custom Vue transport;
- validate the actual uploaded content rather than trusting client MIME type or filename;
- issue expiring, tamper-resistant submission references;
- authorize deletion as well as creation;
- keep cleanup and abandoned-session handling explicit.
Custom endpoints are outside the package route and token guarantees unless the application deliberately implements equivalent controls.