> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Een SPA bouwen met Inertia.js

> Een introductie van Inertia.js v3, dat een Laravel-backend verbindt met een Vue/React/Svelte-frontend. Van het mechanisme dat een SPA zonder API mogelijk maakt tot paginacomponenten, gedeelde data en formulierverwerking — praktisch uitgelegd.

## Wat is Inertia.js?

Je wilt de frontend bouwen met React of Vue, maar je wilt niet apart een API ontwerpen, implementeren en beheren — dat dilemma lost **Inertia.js** op.

Inertia is de "lijm" die het mogelijk maakt om alleen de frontend in React, Vue of Svelte te schrijven, terwijl de serverside routing en controllers intact blijven. Het is geen framework, maar functioneert als een adapterlaag die je bestaande Laravel-app verbindt met een JavaScript-framework.

<Info>
  De nieuwste versie is momenteel Inertia v3 (uitgebracht op 26 maart 2026). De starter kits van Laravel 13 (React, Vue en Svelte) zijn al voorbereid op Inertia.
</Info>

### Het verschil met een klassieke SPA en MPA

| Architectuur                    | Kenmerken                                     | Nadelen                                                        |
| ------------------------------- | --------------------------------------------- | -------------------------------------------------------------- |
| MPA (klassieke Blade-app)       | Eenvoudig, makkelijke integratie met Laravel  | Volledige reload bij elke paginawissel                         |
| SPA (API + gescheiden frontend) | Hoge interactiviteit                          | Dubbel beheer van API-ontwerp, authenticatie en typedefinities |
| **Inertia (moderne monoliet)**  | UX van een SPA + de eenvoud van de serverkant | Eigen leercurve                                                |

Met Inertia geef je data rechtstreeks vanuit je Laravel-controllers door aan Vue- of React-componenten. Je hoeft geen REST API te definiëren, en paginawissels verlopen via XHR in plaats van een volledige browserreload, zodat je de soepele gebruikservaring van een SPA krijgt.

***

## De relatie met de Laravel starter kits

Kies je bij het aanmaken van een project met `laravel new` voor React, Vue of Svelte, dan wordt Inertia automatisch opgezet.

```shell theme={null}
laravel new my-app
# Kies je in de interactieve prompt React / Vue / Svelte, dan krijg je een Inertia-configuratie
```

De starter kits zetten automatisch het volgende klaar.

* `inertiajs/inertia-laravel` (de serverside adapter)
* `@inertiajs/react` / `@inertiajs/vue3` / `@inertiajs/svelte` (de clientadapters)
* De middleware `HandleInertiaRequests`
* Authenticatieschermen zoals inloggen, registreren en wachtwoordherstel (al geïmplementeerd als Inertia-componenten)

Wil je Inertia handmatig toevoegen aan een bestaand project, dan installeer je de serverkant en de clientkant apart. Zie de installatie-instructies in de officiële documentatie voor de details.

***

## De basis van `Inertia::render()`

Om vanuit een Laravel-controller een Inertia-response te retourneren, gebruik je `Inertia::render()`. Het eerste argument is de naam van het JavaScript-component, het tweede de data die je als props doorgeeft.

```php theme={null}
use Inertia\Inertia;

class PostController extends Controller
{
    public function index()
    {
        return Inertia::render('Posts/Index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }

    public function show(Post $post)
    {
        return Inertia::render('Posts/Show', [
            'post' => $post->only('id', 'title', 'content', 'created_at'),
            'author' => $post->user->only('id', 'name'),
        ]);
    }
}
```

<Tip>
  In plaats van `Inertia::render()` kun je ook de helperfunctie `inertia()` gebruiken. Spreek binnen je team één stijl af.
</Tip>

De componentnaam `'Posts/Index'` komt overeen met een bestandspad. Bij React hoort daar `resources/js/Pages/Posts/Index.jsx` bij, bij Vue is dat `resources/js/Pages/Posts/Index.vue`.

***

## De structuur van een paginacomponent

Een Inertia-paginacomponent is een gewoon Vue/React-component. De data die je vanuit de controller doorgeeft, ontvang je als props.

```jsx React theme={null}
// resources/js/Pages/Posts/Index.jsx
import { Link } from '@inertiajs/react'

export default function PostsIndex({ posts }) {
    return (
        <div>
            <h1>Berichten</h1>
            {posts.data.map(post => (
                <article key={post.id}>
                    <h2>
                        <Link href={`/posts/${post.id}`}>{post.title}</Link>
                    </h2>
                </article>
            ))}
        </div>
    )
}
```

```vue Vue theme={null}
<!-- resources/js/Pages/Posts/Index.vue -->
<script setup>
import { Link } from '@inertiajs/vue3'

defineProps({
    posts: Object,
})
</script>

<template>
    <div>
        <h1>Berichten</h1>
        <article v-for="post in posts.data" :key="post.id">
            <h2>
                <Link :href="`/posts/${post.id}`">{{ post.title }}</Link>
            </h2>
        </article>
    </div>
</template>
```

Met het `<Link>`-component verlopen paginawissels via XHR en vermijd je een volledige paginareload. Je schrijft het precies zoals een gewone `<a>`-tag, maar achter de schermen vervangt Inertia alleen het paginacomponent.

***

## Gedeelde data — de middleware `HandleInertiaRequests`

Data die op alle pagina's nodig is (informatie over de ingelogde gebruiker, flashberichten, enz.) definieer je in de `share()`-methode van de middleware `HandleInertiaRequests`.

```php theme={null}
// app/Http/Middleware/HandleInertiaRequests.php
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'appName' => config('app.name'),

            'auth' => [
                'user' => $request->user()
                    ? $request->user()->only('id', 'name', 'email')
                    : null,
            ],

            'flash' => [
                'success' => $request->session()->get('success'),
                'error' => $request->session()->get('error'),
            ],
        ]);
    }
}
```

Gedeelde data wordt automatisch gemerged in de props van elke pagina.

```jsx React theme={null}
// Voorbeeld van toegang vanuit een layoutcomponent
import { usePage } from '@inertiajs/react'

export default function Layout({ children }) {
    const { auth, flash } = usePage().props

    return (
        <main>
            <header>
                {auth.user ? `Ingelogd: ${auth.user.name}` : 'Gast'}
            </header>
            {flash.success && <div className="alert-success">{flash.success}</div>}
            <article>{children}</article>
        </main>
    )
}
```

<Info>
  Gedeelde data wordt bij elk request meegestuurd, dus het is aan te raden om deze te beperken tot het strikt noodzakelijke. Gebruik je lazy evaluatie met `fn()`, dan wordt de data alleen geëvalueerd bij requests die haar echt nodig hebben.
</Info>

***

## Formulieren verzenden en validatiefouten afhandelen

De formulierverwerking van Inertia integreert naadloos met de validatie van Laravel. Met de helper `useForm()` implementeer je het statebeheer, verzenden en de foutweergave van formulieren op een eenvoudige manier.

### Aan de controllerkant

```php theme={null}
class PostController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
        ]);

        Post::create($validated + ['user_id' => auth()->id()]);

        return redirect()->route('posts.index')
            ->with('success', 'Bericht aangemaakt.');
    }
}
```

Treedt er een validatiefout op, dan stuurt Laravel automatisch een redirect naar de formulierpagina en slaat het de foutinformatie op in de sessie. Inertia detecteert dit automatisch en geeft het als de prop `errors` door aan de pagina.

### Aan de frontendkant

```jsx React theme={null}
// resources/js/Pages/Posts/Create.jsx
import { useForm } from '@inertiajs/react'

export default function PostsCreate() {
    const { data, setData, post, processing, errors } = useForm({
        title: '',
        content: '',
    })

    function handleSubmit(e) {
        e.preventDefault()
        post('/posts')
    }

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>Titel</label>
                <input
                    value={data.title}
                    onChange={e => setData('title', e.target.value)}
                />
                {errors.title && <p className="error">{errors.title}</p>}
            </div>

            <div>
                <label>Inhoud</label>
                <textarea
                    value={data.content}
                    onChange={e => setData('content', e.target.value)}
                />
                {errors.content && <p className="error">{errors.content}</p>}
            </div>

            <button type="submit" disabled={processing}>
                {processing ? 'Bezig met verzenden...' : 'Plaatsen'}
            </button>
        </form>
    )
}
```

```vue Vue theme={null}
<!-- resources/js/Pages/Posts/Create.vue -->
<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
    title: '',
    content: '',
})

function submit() {
    form.post('/posts')
}
</script>

<template>
    <form @submit.prevent="submit">
        <div>
            <label>Titel</label>
            <input v-model="form.title" />
            <p v-if="form.errors.title" class="error">{{ form.errors.title }}</p>
        </div>

        <div>
            <label>Inhoud</label>
            <textarea v-model="form.content"></textarea>
            <p v-if="form.errors.content" class="error">{{ form.errors.content }}</p>
        </div>

        <button type="submit" :disabled="form.processing">
            {{ form.processing ? 'Bezig met verzenden...' : 'Plaatsen' }}
        </button>
    </form>
</template>
```

Wanneer er een validatiefout terugkomt, toont `useForm()` de fouten terwijl de ingevoerde gegevens behouden blijven. De gebruiker hoeft het formulier niet opnieuw in te vullen, wat de gebruikservaring verbetert.

***

## Geschikte en minder geschikte use-cases

### Wanneer Inertia geschikt is

* Wanneer een team dat Laravel goed kent een SPA-achtige UI wil bouwen
* Wanneer je authenticatie, autorisatie en validatie centraal aan de Laravel-kant wilt beheren
* Apps waarbij SEO een lage prioriteit heeft, zoals beheerpanelen en interne tools
* Projecten die de kosten van het apart ontwerpen en beheren van een API willen vermijden

### Wanneer het minder geschikt is

* Wanneer meerdere externe clients (zoals mobiele apps) dezelfde API gebruiken
* Contentsites waarbij SEO cruciaal is (op te lossen met SSR, maar dat maakt het complexer)
* Wanneer de frontend door een volledig onafhankelijk team wordt ontwikkeld, zoals bij een micro-frontendarchitectuur

<Tip>
  Inertia ondersteunt ook server-side rendering (SSR). Heb je pagina's waarvoor SEO nodig is, overweeg dan de SSR-optie. Ook in de Laravel starter kits is de SSR-configuratie ingebouwd.
</Tip>

***

## De belangrijkste wijzigingen in Inertia v3

Inertia v3 werd uitgebracht op 26 maart 2026. Dit zijn de belangrijkste wijzigingen ten opzichte van v2.

### Axios geschrapt — naar een lichtgewicht ingebouwde XHR-client

In v3 is Axios geschrapt en vervangen door een lichtere, ingebouwde XHR-client. Voor de meeste applicaties zijn geen codewijzigingen nodig. Axios-interceptors kun je rechtstreeks migreren naar de ingebouwde interceptors. Wil je Axios blijven gebruiken, dan kan dat via de Axios-adapter.

### Eenvoudigere SSR met de `@inertiajs/vite`-plugin

Met de nieuwe Vite-plugin worden het automatisch resolven van paginacomponenten en de SSR-configuratie sterk vereenvoudigd. SSR tijdens de ontwikkeling werkt nu door simpelweg `npm run dev` uit te voeren; een aparte Node-server starten is niet meer nodig.

```bash theme={null}
npm install @inertiajs/vite@^3.0
```

### De `useHttp`-hook — HTTP-requests zonder paginawissel

Met de `useHttp`-hook kun je HTTP-requests naar de server sturen zonder een paginawissel (Inertia-visit) te veroorzaken. Handig in situaties waarin je wilt communiceren terwijl de huidige pagina behouden blijft, zoals het opslaan vanuit een modal of achtergrondverwerking.

### Optimistische UI-updates (optimistic updates)

Optimistische updates worden nu ondersteund in `useForm` en op routerniveau. De UI wordt direct bijgewerkt zonder op het antwoord van de server te wachten, en bij een mislukking volgt automatisch een rollback.

### Layout props

Met de `useLayoutProps`-hook kun je nu data doorgeven van een paginacomponent naar een persistente layout. Het aansturen van de layoutstatus vanaf specifieke pagina's — waarvoor je voorheen was aangewezen op gedeelde data of paginaprops — schrijf je nu eenvoudig.

### Gewijzigde vereisten

In v3 zijn de volgende minimumversies verhoogd.

| Onderdeel | v2   | v3   |
| --------- | ---- | ---- |
| PHP       | 8.1+ | 8.2+ |
| Laravel   | 10+  | 11+  |
| React     | 18+  | 19+  |
| Svelte    | 4+   | 5+   |

### `Inertia::lazy()` geschrapt

`Inertia::lazy()`, dat in v2 al deprecated was, is in v3 volledig verwijderd. Gebruik in plaats daarvan `Inertia::optional()`.

```php theme={null}
// v2 (deprecated)
'users' => Inertia::lazy(fn () => User::all()),

// v3
'users' => Inertia::optional(fn () => User::all()),
```

### Gewijzigde eventnamen

| v2          | v3              |
| ----------- | --------------- |
| `invalid`   | `httpException` |
| `exception` | `networkError`  |

### De `future`-optie geschrapt

Het `future`-configuratieblok, dat in v2 als experimentele optie werd aangeboden, is geschrapt. Al deze opties zijn nu standaard ingeschakeld. Verwijder het `future`-blok uit de configuratie van `createInertiaApp`.

<Info>
  Zie de <a href="https://inertiajs.com/docs/v3/getting-started/upgrade-guide">officiële upgradegids</a> voor de details van het upgraden vanaf v2. Voor v2 worden bugfixes geleverd tot 26 september 2026 en beveiligingsfixes tot 26 maart 2027.
</Info>

***

## Conclusie

Inertia.js is een tool die inspeelt op de reële behoefte: "Laravel houden zoals het is, maar de frontend in React/Vue schrijven". De eenvoud waarmee je zonder een API te ontwerpen data rechtstreeks van controllers aan componenten doorgeeft, is de grootste kracht.

Dat de starter kits van Laravel 13 op Inertia zijn gebouwd, laat zien dat de positie van Inertia binnen het Laravel-ecosysteem stevig verankerd is. Waar Livewire de aanpak is om dynamische UI's met alleen PHP te bouwen, is Inertia de aanpak waarmee je de kracht van JavaScript-frameworks benut zonder de eenvoud van de serverkant op te geven.

Welke je kiest hangt af van de skillset van je team en de eisen van je project, maar wil je "met behoud van je Laravel-controllers schermen bouwen in React", dan is Inertia de meest natuurlijke keuze.

<Card title="Officiële documentatie van Inertia.js" icon="book-open" href="https://inertiajs.com">
  Raadpleeg de officiële documentatie voor alle functies van Inertia v3.
</Card>


## Related topics

- [⚡Introductie van Livewire 4 — reactieve UI's bouwen zonder JavaScript](/nl/blog/livewire-introduction.md)
- [Starter kits](/nl/starter-kits.md)
- [Frontend](/nl/frontend.md)
- [Introductie Vue.js — de basis voor Inertia × Laravel](/nl/blog/vue-introduction.md)
- [Introductie React — de basis voor Inertia × Laravel](/nl/blog/react-introduction.md)
