Mastering Forms in Laravel + Inertia + Vue 3
Creating forms in a Vue single-page application that communicates with a Laravel backend can quickly become complex. Traditional methods often involve manual error mapping, axios calls, and state management. However, Inertia's useForm() simplifies these processes significantly, allowing for more efficient form handling.
Quick Setup
To begin, set up a Laravel project with Inertia and Vue 3 pre-configured:
laravel new my-app --using=laravel/vue-starter-kit
cd my-app
npm install
Run the servers:
php artisan serve # Laravel on :8000
npm run dev # Vite on :5173
Visit http://localhost:8000 to see the default authentication scaffolding in action. If you're integrating Inertia into an existing project, refer to the official guide.
Understanding useForm()
useForm() handles form data, loading states, error mapping, and resetting automatically:
import { useForm } from '@inertiajs/vue3'
const form = useForm({
name: 'John',
email: 'john@example.com',
avatar: null,
})
Key functions include:
form.post(route('profile.update'))form.reset()form.clearErrors()
These methods streamline form management without manual axios calls.
Creating and Validating Forms
First, define the Vue component:
<script setup>
import { useForm } from '@inertiajs/vue3'
const props = defineProps({
user: Object
})
const form = useForm({
name: props.user.name,
email: props.user.email,
})
function submit() {
form.post(route('profile.update'))
}
</script>
<template>
<form @submit.prevent="submit">
<div>
<label>Name</label>
<input v-model="form.name" type="text" />
<span v-if="form.errors.name">{{ form.errors.name }}</span>
</div>
<div>
<label>Email</label>
<input v-model="form.email" type="email" />
<span v-if="form.errors.email">{{ form.errors.email }}</span>
</div>
<button type="submit" :disabled="form.processing">
{{ form.processing ? 'Saving...' : 'Save Changes' }}
</button>
</form>
</template>
In Laravel, create a FormRequest for validation:
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email,' . auth()->id()],
];
}
File Uploads with Progress
To handle file uploads, update useForm():
const form = useForm({
name: props.user.name,
email: props.user.email,
avatar: null,
})
For the file input:
<div>
<label>Avatar</label>
<input type="file" accept="image/*" @change="form.avatar = $event.target.files[0]" />
<span v-if="form.errors.avatar">{{ form.errors.avatar }}</span>
</div>
To track upload progress:
<div v-if="form.progress">
<progress :value="form.progress.percentage" max="100">
{{ form.progress.percentage }}%
</progress>
<span>{{ form.progress.percentage }}%</span>
</div>
On the Laravel side, update validation rules for file uploads:
'avatar' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
Conclusion
Inertia's useForm() offers a robust mental model for form handling in Vue applications, making it the go-to solution for modern web development.