Laravel Inertia.js with Vue/React: Step-by-Step Guide

·

·

Inertia bridges Laravel backends and modern JavaScript frontends. You write controllers that return Inertia responses instead of JSON APIs, while Vue or React renders pages with client-side navigation.

Installing Inertia with Breeze

Laravel Breeze offers Inertia stacks for Vue and React with authentication scaffolding.

The server adapter inertiajs/inertia-laravel pairs with @inertiajs/vue3 or @inertiajs/react on the frontend.

Step 1: Create a fresh Laravel app with Breeze

Run composer require laravel/breeze –dev then php artisan breeze:install vue or react –inertia.

npm install && npm run dev compiles assets. php artisan migrate sets up auth tables.

composer require laravel/breeze --dev
php artisan breeze:install vue --inertia
npm install && npm run dev

Step 2: Understand the Inertia response

Controllers return Inertia::render('Dashboard', ['stats' => $stats]) instead of view() or json().

Props serialize to JSON embedded in the page. Subsequent visits use XHR with partial data loads.

return Inertia::render('Dashboard', [
    'stats' => $stats,
]);

Step 3: Configure the root Blade template

resources/views/app.blade.php includes @vite directives and @inertia directive where the SPA mounts.

The @inertiaHead slot manages meta tags for SEO on JavaScript-driven pages.

@vite(['resources/js/app.js'])
@inertia

Step 4: Define routes as usual

routes/web.php stays in Laravel. Middleware, policies, and sessions work identically to Blade apps.

Only the response format changes. Authorization still runs server-side before props are shared.

Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');

Vue and React Page Components

Pages live in resources/js/Pages/. File-based naming maps Dashboard.vue to Inertia::render('Dashboard').

Layouts wrap pages with navigation and flash message slots.

Step 1: Create a page component

Add resources/js/Pages/Posts/Index.vue with <script setup> and defineProps({ posts: Array }).

Link between pages using <Link href="/posts/create"> from @inertiajs/vue3 without full reloads.

<script setup>
defineProps({ posts: Array });
</script>

Step 2: Share global data with HandleInertiaRequests

In app/Http/Middleware/HandleInertiaRequests.php add auth user and flash to shared() array.

Shared props appear on every page without passing them from each controller.

public function share(Request $request): array
{
    return array_merge(parent::share($request), [
        'auth' => ['user' => $request->user()],
    ]);
}

Step 3: Handle forms with useForm

Import useForm from @inertiajs/vue3. Bind fields and call form.post('/posts') for validation errors inline.

useForm tracks processing state for disabling submit buttons during requests.

const form = useForm({ title: '', body: '' });
form.post('/posts');

Step 4: Lazy load heavy props

Use optional closure props: Inertia::render('Report', ['data' => fn () => HeavyQuery::run()]).

Lazy props defer expensive queries until the client requests them, speeding initial page load.

Inertia::render('Report', [
    'data' => fn () => ReportService::build(),
]);

Deployment and SSR

Production builds run npm run build. Configure SSR optionally for faster first paint and SEO.

Asset versioning through Vite manifest prevents stale JavaScript after deploys.

Step 1: Build assets for production

Run npm run build to compile hashed assets into public/build. Vite manifest maps entry points.

Set APP_ENV=production and ensure @vite loads compiled assets, not the dev server.

npm run build

Step 2: Enable Inertia SSR optionally

Run php artisan inertia:start-ssr after configuring ssr bundle in vite.config.js.

SSR renders initial HTML on the server. Subsequent navigation remains client-driven.

php artisan inertia:start-ssr

Step 3: Version assets with mix manifest

Inertia middleware shares version from parent::version() busting client cache on deploy.

Bump version when only PHP changes if props schema changes require fresh JS.

php artisan config:cache

Step 4: Organize Ziggy routes for frontend

Install tightenco/ziggy to expose named Laravel routes to JavaScript route() helper.

Frontend links stay in sync when Laravel route names change.

composer require tightenco/ziggy
php artisan ziggy:generate

Aadyanex builds production-ready Laravel applications with clean architecture, secure APIs, and long-term support.