Web Development 5-8 minutes

Inertia 3 with Laravel 13: Build a SPA Without Writing an API

Diego Cortés
Diego Cortés
Full Stack Developer & SEO Specialist
Share:
Inertia 3 with Laravel 13: Build a SPA Without Writing an API

Inertia 3.0.0 shipped on March 26, 2026, and Laravel 13's official starter kits already include it for React, Vue, and Svelte: you render components from the controller, with no API or JSON endpoints. This guide shows you how to build a SPA with Inertia 3 and Laravel 13 from scratch, covering forms, optimistic updates, and SSR.

What Inertia Is and Why It Fits Laravel 13

The Server-Driven SPA: the Controller Decides the Page

Inertia is a library that connects Laravel to a frontend framework such as React, Vue, or Svelte without building a separate API. The controller returns two things: the props, meaning the data the page needs, and the name of the JavaScript component that should render them. The frontend paints that component, and when the user navigates to another route, Inertia swaps only the part of the page that changes, without reloading the whole document. The result is the SPA experience with Laravel's classic workflow: routes, controllers, and Blade as the entry point.

Inertia vs. Livewire vs. a Classic API + SPA

The difference from a classic SPA is that you do not write JSON endpoints or manage API authentication: Laravel routes return pages directly. The difference from Livewire is the other side of the coin: Livewire keeps state and interactive logic on the server in PHP, while Inertia hands interactivity to the client in JavaScript. If your team is strong in React or Vue and you want a highly dynamic UI, Inertia fits; if you prefer to avoid JavaScript, Livewire remains the path.

What Inertia 3 Brings (March 2026)

Goodbye Axios: Built-in XHR Client and a Smaller Bundle

Version 3 drops the Axios dependency and uses its own XHR client built into the library. That means a smaller bundle and one less thing to maintain, because the request client is now part of the Inertia package itself. For most applications you will not notice any difference when migrating, except in the final size of the files that reach the browser.

SSR in Development Without a Separate Node Server

Server-side rendering was historically the most awkward part of Inertia: you had to spin up a separate Node.js server just to test it. In Inertia 3, SSR works in development without that extra step, which simplifies the daily workflow. You configure the renderer once, and both locally and in production the pages are served rendered when needed.

The useHttp Hook for Standalone Requests

useHttp fills a gap Inertia has had for a long time: HTTP requests that do not trigger navigation, such as saving a file, validating an email, or querying an endpoint. It returns reactive state with processing, errors, progress, and isDirty, with the same developer experience as useForm, and it integrates real-time validation with Precognition 2.x when combined with the Laravel package.

Optimistic Updates in router, useForm, and useHttp

Optimistic updates arrive at all three entry points: router, useForm, and useHttp. The interface reflects the change instantly, before the server confirms the request, and if the response fails, the state automatically reverts to the previous value. That is the technique that makes an application feel instant even on slow connections.

The @inertiajs/vite Plugin and Vite 8

The new @inertiajs/vite plugin replaces the previous manual configuration: it manages page resolution and SSR configuration automatically, and adds support for Vite 8. In your vite.config.js file you no longer have to point out where your components live or what your server entry file is called; the plugin infers it from the project structure.

Starting with Laravel 13's Official Starter Kit

laravel new with React, Vue, or Svelte

The fastest way to get Inertia 3 running is to use Laravel 13's official starter kits, which include React, Vue, and Svelte already configured with Inertia 3. For example, the Svelte kit combines Svelte 5, TypeScript, Inertia 3, and shadcn-svelte, with all the code living in your application. For this tutorial we chose React, the most common option:

composer create-project laravel/laravel my-app
cd my-app
composer require laravel/breeze --dev
php artisan breeze:install react --ssr
npm install
npm run dev

Breeze installs the starter kit with Inertia 3 and sets up SSR for you. Within minutes you have an application with login, registration, and a sample dashboard running on React, without writing a single API route.

Project Structure of the Generated App

The generated project clearly separates the two halves. On the backend, controllers return pages with Inertia::render and routes are defined in routes/web.php as always. On the frontend, the resources/js folder contains the components: app.tsx as the entry point, pages/ with one folder per page, and the shared layout components. The vite.config.js file includes the @inertiajs/vite plugin and the SSR configuration.

Your First Inertia Page: Controller, Props, and Component

Inertia::render from the Controller

An Inertia page is built in the controller by naming the component and passing the data. For example, an article listing:

use Inertia\Inertia;

public function index()
{
    return Inertia::render('Articles/Index', [
        'articles' => Article::published()->latest()->get(),
    ]);
}

On the frontend, the Articles/Index component receives the props and renders them with React. Inertia takes care of converting the PHP data to JSON and passing it to the component on every visit.

Reload-Free Navigation Between Pages

Links are created with Inertia's Link component instead of a plain anchor tag:

import { Link } from '@inertiajs/react';

<Link href="/articles/1">Read article</Link>

When the user clicks the link, Inertia intercepts the request, asks the server only for the new page's data, and updates the content without reloading the browser. History, scroll, and focus behave like a traditional navigation, so you lose none of the behavior users expect.

Forms with useForm and Validation

Server-Side Validation Errors

For forms, Inertia offers the useForm hook. You send the data with the post method, and if the server returns Laravel validation errors, they arrive automatically in the errors prop:

const { data, setData, post, errors, processing } = useForm({
    title: '',
    body: '',
});

function submit(e) {
    e.preventDefault();
    post('/articles');
}

The form keeps its state between submissions, shows errors field by field, and disables the button while processing is true. All the validation logic still lives in Laravel Form Requests, just like in a classic application.

Standalone Requests with useHttp and Upload Progress

For actions that do not navigate, like uploading an image or checking availability, useHttp makes the request and exposes the progress. In a file upload, the progress property lets you render a real progress bar:

const upload = useHttp();

function handleUpload(e) {
    upload.post('/uploads', {
        file: e.target.files[0],
        forceFormData: true,
    });
}

{upload.progress && <progress value={upload.progress.percentage} max="100" />}

The request fires without changing pages, and the component reacts to processing, errors, and progress declaratively.

Optimistic Updates in Practice

The clearest case for an optimistic update is marking an article as a favorite. With useForm or router you can apply the change in the interface before the server responds and revert it if it fails:

router.put(`/articles/${article.id}/favorite`, {}, {
    optimistic: {
        favorite: !article.favorite,
    },
    onError: () => {
        // Inertia automatically reverts to the previous value
    },
});

The user sees the heart filled instantly; if the request fails, Inertia restores the original state with no extra code. That is the difference between an app that feels reactive and one that seems to ask the server for permission on every interaction.

SSR in Production with Inertia 3

Server-side rendering improves SEO and the first impression: the HTML arrives complete in the browser and React hydrates it afterward. With Inertia 3, the production flow is reduced to installing the server package, building the SSR entry file, and starting the process with a process manager:

npm run build
node bootstrap/ssr/ssr.mjs

The @inertiajs/vite plugin generates and configures this file automatically. In production you should supervise the SSR process with systemd or an equivalent manager so it restarts if it crashes, just as you would with a queue worker.

Inertia 3 vs. Livewire 4: When to Choose Each

The decision between Inertia 3 and Livewire 4 is mostly about team and product. Livewire keeps all the logic in PHP and the state on the server: perfect if your team is backend-focused and interactions are moderate. Inertia requires a JavaScript frontend, but gives you full control over the UI with React, Vue, or Svelte, ideal for highly dynamic interfaces, animations, and reusable components. Laravel 13's starter kits include both, so you can try each in minutes and decide with data, not opinions.

Conclusion

Inertia 3 with Laravel 13 gives you a modern SPA with React, Vue, or Svelte without maintaining a separate API: the controller still decides which page renders, and useHttp, optimistic updates, and SSR solved in development remove the library's classic friction points. Start with an official starter kit, and if you want to compare it with the server-driven alternative, check out the Livewire 4 guide we already published on this blog.

Categories