<template>
<form @submit.prevent="form.post(route('posts.store'))">
<div>
<label>Title</label>
<input v-model="form.title" type="text" />
<div v-if="form.errors.title">{{ form.errors.title }}</div>
</div>
<div>
<label>Body</label>
<textarea v-model="form.body"></textarea>
<div v-if="form.errors.body">{{ form.errors.body }}</div>
</div>
<button type="submit" :disabled="form.processing">
{{ form.processing ? 'Saving...' : 'Save Post' }}
</button>
<div v-if="form.progress">
Upload progress: {{ form.progress.percentage }}%
</div>
</form>
</template>
<script setup>
import { useForm } from '@inertiajs/vue3'
const form = useForm({
title: '',
body: '',
})
</script>
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Inertia;
class PostController extends Controller
{
public function index()
{
return Inertia::render('Posts/Index', [
'posts' => Post::query()
->with('author:id,name')
->select('id', 'title', 'excerpt', 'user_id', 'published_at')
->latest()
->paginate(20),
// Lazy evaluation - only loads when requested
'filters' => fn () => request()->only(['search', 'category']),
]);
}
public function show(Post $post)
{
$post->load(['author', 'comments.author']);
return Inertia::render('Posts/Show', [
'post' => $post,
'can' => [
'update' => auth()->user()?->can('update', $post),
'delete' => auth()->user()?->can('delete', $post),
],
]);
}
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|max:255',
'body' => 'required',
]);
$post = $request->user()->posts()->create($validated);
return redirect()
->route('posts.show', $post)
->with('success', 'Post created!');
}
}
<template>
<div>
<h1>Posts</h1>
<div v-for="post in posts.data" :key="post.id">
<Link :href="route('posts.show', post.id)">
<h2>{{ post.title }}</h2>
</Link>
<p>{{ post.excerpt }}</p>
<small>By {{ post.author.name }}</small>
</div>
<Pagination :links="posts.links" />
</div>
</template>
<script setup>
import { Link } from '@inertiajs/vue3'
import Pagination from '@/Components/Pagination.vue'
defineProps({
posts: Object,
})
</script>
Inertia.js bridges Laravel backends with Vue, React, or Svelte frontends without building a separate API. Server-side controllers return Inertia responses containing component names and props. The frontend renders those components with reactive frameworks. Navigation uses Inertia's router for client-side transitions without full page loads. Shared data like auth user and flash messages pass via middleware. Form submissions use Inertia's form helpers with automatic validation error handling. Lazy data evaluation defers expensive computations until needed. Partial reloads fetch only changed data. This architecture provides SPA experience while maintaining Laravel's routing and controller patterns. No API routes, no state management—just modern frontends with Laravel backends.