Handling Server-Side Validation and Input Preservation in Inertia.js
Learn how to implement server-side validation in Inertia.js using Vue 3 and Laravel. This guide covers using the useForm hook to preserve input and display field-specific errors without page reloads.
16 Apr 2026, 05:33 UTC

The Problem: Losing Form State on Validation Failure
In traditional multi-page applications, a validation error triggers a full page reload, often clearing user input unless the developer manually repopulates fields using session data. In a Single Page Application (SPA) context using Inertia.js, the goal is to maintain the current application state, display specific field errors, and preserve every character the user typed without a browser refresh.
Prerequisites
- Laravel 10+ with the Inertia server-side adapter installed.
- Vue 3 configured with the
@inertiajs/vue3package. - Inertia Middleware: The
HandleInertiaRequestsmiddleware must be registered in yourapp/Http/Kernel.phpwithin thewebmiddleware group to ensure validation errors are shared globally with the frontend.
Implementing the Validation Workflow
1. Define the Server-Side Controller
Laravel's built-in validation automatically redirects back to the previous page upon failure. When using Inertia, this redirect carries the validation errors and the "old" input back to the frontend automatically via the session.
// app/Http/Controllers/ContactController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
class ContactController extends Controller
{
public function store(Request $request)
{
// Laravel validates and automatically redirects back with errors if it fails
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email',
'message' => 'required|min:10',
]);
// Process the validated data (e.g., save to DB)
// Contact::create($validated);
return Redirect::route('contact.success');
}
}
2. Create the Vue Form Component
The useForm helper is the primary tool for managing form state in Inertia. It tracks the data, processing state, and server-side errors in a single reactive object.
<script setup>
import { useForm } from '@inertiajs/vue3';
const form = useForm({
name: '',
email: '',
message: '',
});
const submit = () => {
form.post('/contact', {
preserveScroll: true,
onSuccess: () => form.reset(),
});
};
</script>
<template>
<form @submit.prevent="submit">
<div>
<label>Name:</label>
<input v-model="form.name" type="text" :class="{'border-red-500': form.errors.name}" />
<p v-if="form.errors.name" class="text-red-500">{{ form.errors.name }}</p>
</div>
<div>
<label>Email:</label>
<input v-model="form.email" type="email" />
<p v-if="form.errors.email" class="text-red-500">{{ form.errors.email }}</p>
</div>
<div>
<label>Message:</label>
<textarea v-model="form.message"></textarea>
<p v-if="form.errors.message" class="text-red-500">{{ form.errors.message }}</p>
</div>
<button type="submit" :disabled="form.processing">
{{ form.processing ? 'Sending...' : 'Submit' }}
</button>
</form>
</template>
Verification and Diagnostics
To confirm the implementation is working correctly, perform the following checks:
- Network Inspection: Open Browser DevTools > Network tab. Submit the form with empty fields. You should see a
POSTrequest followed by a302redirect. The subsequentGETrequest (the redirect back to the form) will contain theX-Inertiaheader and a JSON payload containing theprops.errorsobject. - State Persistence: Verify that the text entered in the fields remains present after the error messages appear. This confirms that
v-modelis correctly bound to theuseFormstate and that Laravel's session is preserving the input. - UI Feedback: Ensure the submit button is disabled during the
form.processingstate to prevent duplicate submissions.
Recovery and Troubleshooting
Errors are not appearing in the UI
Check if the HandleInertiaRequests middleware is active. Inertia relies on this middleware to share validation errors from the Laravel session with the Vue props. If the middleware is missing, the form.errors object will remain empty even if the server returns validation failures.
Input is cleared on failure
Ensure you are using v-model bound to the useForm object (e.g., v-model="form.name") rather than a local ref. If you are using a custom request class, ensure you haven't overridden the failedValidation method in a way that prevents the redirect back with input.
Limitations
- Large File Uploads: When submitting files, you must use
form.post. Note that Laravel'sold()helper does not preserve uploaded files for security reasons; users must re-select files if validation fails. - Nested Arrays: For forms with dynamic arrays of inputs, validation errors are returned as dot-notation keys (e.g.,
users.0.name). You will need to access these specifically in Vue usingform.errors['users.0.name'].
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.