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

# Introductie React — de basis voor Inertia × Laravel

> Een beginnersgids voor het gebruik van React met Laravel + Inertia.js. Van de historische route van laravel/ui tot de huidige starter kits, met een praktische uitleg van Inertia React-hooks zoals useForm en usePage.

## Wat is React

React is een JavaScript-library voor het bouwen van gebruikersinterfaces, ontwikkeld en onderhouden door Meta (voorheen Facebook). Kenmerkend zijn de declaratieve UI-beschrijving en de **componentgebaseerde** architectuur; React wordt breed ingezet, van kleine widgets tot volledige SPA's.

De kern van React is efficiënt herrenderen via de **virtual DOM**. Wanneer de state verandert, past React alleen de verschillen toe op de DOM, zodat je de DOM nooit handmatig hoeft te manipuleren.

<Info>
  Deze pagina behandelt de combinatie van React 19 en Inertia v3. De starter kits van Laravel 13 gebruiken deze opzet standaard.
</Info>

### JSX en TSX

React-componenten schrijf je in de syntaxis **JSX** (JavaScript XML). Je schrijft HTML-achtige notatie direct in JavaScript.

```jsx theme={null}
// Voorbeeld van JSX
function Greeting({ name }) {
    return <h1>Hallo, {name}!</h1>
}
```

In de starter kits is **TypeScript** (`.tsx`) de standaard. Dankzij typedefinities wordt de IDE-autocompletion sterker en vind je bugs eerder.

```tsx theme={null}
// Voorbeeld van TSX (TypeScript)
type Props = {
    name: string
}

function Greeting({ name }: Props) {
    return <h1>Hallo, {name}!</h1>
}
```

<Tip>
  De React starter kit van Laravel gebruikt standaard TypeScript + TSX. Alle voorbeelden op deze pagina zijn daarom in TSX geschreven.
</Tip>

***

## De positie binnen Laravel

### Geschiedenis

De relatie tussen React en Laravel is iets korter dan die met Vue, maar inmiddels wordt React gelijkwaardig of zelfs prominenter behandeld.

```mermaid theme={null}
timeline
    title Laravel en React door de jaren heen
    2019 : Laravel 6 — React-scaffold toegevoegd aan laravel/ui
    2021 : Laravel 8 — Inertia React-stack toegevoegd aan Breeze
    2022 : Laravel 9 — Overstap naar Vite
    2025 : Laravel 12 — Vernieuwde starter kits (React / Vue)
    2026 : Laravel 13 — Starter kits met Inertia v3
```

Bij **Laravel 6 (2019)** werd de authenticatiescaffolding afgesplitst naar de package `laravel/ui`, en werd naast Vue ook een React-versie van de scaffold aangeboden. In die tijd was Vue echter dominant en had de React-versie weinig zichtbaarheid.

Met de toevoeging van de Inertia + React-stack aan **Laravel Breeze (2021)** begon de serieuze adoptie, en bij de vernieuwing van de starter kits in **Laravel 12 (2025)** kwam React volledig op gelijke voet met Vue te staan (het wordt zelfs als eerste getoond).

### De huidige mainstream: Inertia × React

De belangrijkste manier om React in Laravel te gebruiken is tegenwoordig **Inertia × React**. Inertia realiseert een "moderne monoliet"-architectuur waarbij je zonder een API te ontwerpen data rechtstreeks vanuit Laravel-controllers aan React-componenten doorgeeft.

```mermaid theme={null}
graph LR
    Browser["Browser"]
    Inertia["Inertia.js<br>(adapterlaag)"]
    Laravel["Laravel<br>(controller)"]
    React["React<br>(paginacomponent)"]

    Browser <-->|XHR / volledige paginalading| Inertia
    Inertia <-->|Inertia-response| Laravel
    Inertia -->|props| React
    React -->|rendering| Browser
```

***

## Setup

### Via een starter kit (aanbevolen)

Voor een nieuw project is een starter kit de makkelijkste route.

```shell theme={null}
laravel new my-app
```

Kies je in de interactieve prompt voor **React**, dan wordt dit allemaal automatisch opgezet:

* `inertiajs/inertia-laravel` (server-side adapter)
* `@inertiajs/react` (clientadapter)
* `react` + `react-dom` (React 19 zelf)
* `@vitejs/plugin-react` (Vite-plugin)
* TypeScript + `@types/react`
* Tailwind CSS + de shadcn/ui-componentlibrary
* De middleware `HandleInertiaRequests`
* Authenticatieschermen zoals inloggen en registreren (al geïmplementeerd met Inertia + React + TypeScript)

### Handmatige installatie

Voeg je Inertia toe aan een bestaand project, dan installeer je de server- en clientzijde afzonderlijk.

```shell theme={null}
# Serverzijde (PHP)
composer require inertiajs/inertia-laravel

# Clientzijde (JavaScript)
npm install @inertiajs/react react react-dom
npm install --save-dev @vitejs/plugin-react @types/react @types/react-dom typescript
```

Voeg vervolgens de React-plugin toe aan `vite.config.ts`.

```ts theme={null}
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import react from '@vitejs/plugin-react'

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.tsx'],
            refresh: true,
        }),
        react(),
    ],
})
```

Start de Inertia-app in `resources/js/app.tsx`.

```tsx theme={null}
import { createInertiaApp } from '@inertiajs/react'
import { createRoot } from 'react-dom/client'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'

createInertiaApp({
    resolve: (name) =>
        resolvePageComponent(
            `./pages/${name}.tsx`,
            import.meta.glob('./pages/**/*.tsx'),
        ),
    setup({ el, App, props }) {
        createRoot(el).render(<App {...props} />)
    },
})
```

<Info>
  Zie de [officiële Inertia-documentatie](https://inertiajs.com/installation) voor de details van een handmatige installatie (zoals het instellen van de roottemplate en het registreren van de middleware).
</Info>

***

## Directorystructuur

In de starter kits plaats je React-paginacomponenten in de directory `resources/js/pages/`.

```
resources/js/
├── app.tsx            # Startpunt van de Inertia-app
├── bootstrap.ts
├── components/        # Herbruikbare UI-componenten
│   ├── ui/            # shadcn/ui-componenten
│   └── ...
├── hooks/             # Custom React-hooks
├── layouts/           # Layoutcomponenten
│   ├── app-layout.tsx
│   └── auth-layout.tsx
├── lib/               # Utilityfuncties en configuratie
├── pages/             # Inertia-paginacomponenten (corresponderen met controllernamen)
│   ├── auth/
│   │   ├── login.tsx
│   │   └── register.tsx
│   ├── dashboard.tsx
│   └── posts/
│       ├── index.tsx
│       ├── create.tsx
│       └── show.tsx
└── types/             # TypeScript-typedefinities
```

Schrijf je `Inertia::render('posts/index', [...])`, dan is `resources/js/pages/posts/index.tsx` de bijbehorende component.

***

## De basis van de JSX-syntaxis

React gebruikt JSX: een stijl waarbij je HTML-achtige syntaxis in JavaScript schrijft. Hier zijn de patronen die je minimaal moet kennen om de code van de starter kits te kunnen lezen.

#### `{}` — variabelen invoegen

Binnen JSX gebruik je `{}` om JavaScript-waarden of -expressies in te voegen.

```tsx theme={null}
const name = 'wereld'
const count = 3

return (
    <div>
        <p>Hallo, {name}!</p>
        <p>Het dubbele is {count * 2}</p>
    </div>
)
```

#### Voorwaarden — `&&` en de ternaire operator

React kent geen directive zoals `v-if`. Voor eenvoudige voorwaarden gebruik je de `&&`-operator, voor if/else de ternaire operator `? :`.

```tsx theme={null}
{/* Alleen tonen als de voorwaarde true is */}
{isLoggedIn && <p>Welkom!</p>}

{/* if / else */}
{isLoggedIn ? <p>Welkom!</p> : <a href="/login">Inloggen</a>}

{/* if / else if / else */}
{isLoggedIn
    ? <p>Welkom!</p>
    : role === 'admin'
        ? <p>Ingelogd als beheerder</p>
        : <a href="/login">Inloggen</a>
}
```

#### Lijsten renderen — `.map()`

Voor het renderen van lijsten gebruik je `Array.map()`. Geef altijd de prop `key` op voor efficiënte diff-updates.

```tsx theme={null}
<ul>
    {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
    ))}
</ul>
```

Dit komt overeen met `v-for :key` in Vue of `{#each}` in Svelte.

#### `className` — CSS-klassenamen

Omdat JSX naar JavaScript wordt gecompileerd, is `class` een gereserveerd woord. Voor CSS-klassen gebruik je `className`.

```tsx theme={null}
{/* HTML: <div class="container"> */}
<div className="container">...</div>
```

#### Eventhandlers — camelCase

Eventattributen in JSX zijn in camelCase en krijgen een functiereferentie.

```tsx theme={null}
<button onClick={handleClick}>Klik</button>

{/* Standaardgedrag van het formulier annuleren */}
<form onSubmit={(e) => { e.preventDefault(); handleSubmit() }}>
    ...
</form>
```

***

## De basis van paginacomponenten

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

### Controller

```php theme={null}
// app/Http/Controllers/PostController.php
use Inertia\Inertia;
use App\Models\Post;

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

### React-paginacomponent

```tsx theme={null}
// resources/js/pages/posts/index.tsx
import { Link } from '@inertiajs/react'

type Post = {
    id: number
    title: string
    created_at: string
}

type Props = {
    posts: {
        data: Post[]
        // paginate(10) geeft een object terug met ook paginatie-informatie
        // current_page, last_page, per_page, total enz. zijn ook beschikbaar
    }
}

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

Je ontvangt de props simpelweg als argument van de component en kunt de data uit de controller direct gebruiken. Een REST API definiëren is niet nodig.

***

## De `Link`-component

Met de `<Link>`-component van `@inertiajs/react` verlopen paginanavigaties via XHR, zodat een volledige browserreload wordt vermeden.

```tsx theme={null}
import { Link } from '@inertiajs/react'

export default function PostsIndex() {
    return (
        <div>
            {/* Basislink */}
            <Link href="/posts">Postoverzicht</Link>

            {/* Link met de POST-methode (bijv. verwijderen) */}
            <Link href="/posts/1" method="delete" as="button">
                Verwijderen
            </Link>

            {/* Preload (vooraf ophalen bij hover) */}
            <Link href="/posts/1" preload>
                Post bekijken
            </Link>
        </div>
    )
}
```

Je schrijft hem net als een gewone `<a>`-tag, maar op de achtergrond vervangt Inertia alleen de paginacomponent, waardoor het aanvoelt als een SPA.

***

## De `Form`-component

De `<Form>`-component van `@inertiajs/react` is de aanbevolen stijl voor formulierverzending die in de authenticatieschermen van de starter kits wordt gebruikt. Je geeft `action` en `method` op als props en ontvangt `errors` en `processing` via een children-functie (render prop).

### Basisgebruik

```tsx theme={null}
import { Form } from '@inertiajs/react'

export default function PostCreate() {
    return (
        <Form action="/posts" method="post" className="flex flex-col gap-4">
            {({ errors, processing }) => (
                <>
                    <div>
                        <label htmlFor="title">Titel</label>
                        <input id="title" name="title" type="text" required />
                        {errors.title && <p className="error">{errors.title}</p>}
                    </div>

                    <div>
                        <label htmlFor="content">Inhoud</label>
                        <textarea id="content" name="content" />
                        {errors.content && <p className="error">{errors.content}</p>}
                    </div>

                    <button type="submit" disabled={processing}>
                        {processing ? 'Versturen...' : 'Posten'}
                    </button>
                </>
            )}
        </Form>
    )
}
```

De children van `<Form>` is een functie `({ errors, processing }) => JSX` (het render-prop-patroon). De `Form`-component berekent deze waarden automatisch en geeft ze door. Voor de formuliervelden gebruik je geen `onChange`-handler maar het native HTML-attribuut `name`, zodat de standaard formulierdataverzameling van de browser werkt.

### Het patroon van de starter kits

De starter kits gebruiken [Wayfinder](/nl/blog/wayfinder-introduction) om routes als objecten te beheren. `store.form()` geeft een object terug met de `action` en `method` van het routeobject, dat je in `<Form>` spreadt.

```tsx theme={null}
import { Form } from '@inertiajs/react'
import { store } from '@/routes/login'

export default function Login() {
    return (
        <Form
            {...store.form()}
            resetOnSuccess={['password']}
            className="flex flex-col gap-6"
        >
            {({ errors, processing }) => (
                <>
                    {/* Inhoud van het formulier */}
                </>
            )}
        </Form>
    )
}
```

Velden die je opgeeft in `resetOnSuccess` worden bij een geslaagde verzending automatisch gereset. Gebruik dit voor velden die je na het verzenden leeg wilt maken, zoals wachtwoordvelden.

<Info>
  Gebruik je Wayfinder niet, dan werkt het net zo goed als je direct een URL doorgeeft, zoals `action="/login"`.
</Info>

***

## De `useForm`-hook

Voor formulierverwerking gebruik je de `useForm`-hook van `@inertiajs/react`. Statebeheer van het formulier, verzenden en het tonen van validatiefouten implementeer je hiermee eenvoudig.

### Aan de controllerkant

```php theme={null}
// app/Http/Controllers/PostController.php
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', 'Post aangemaakt.');
    }
}
```

### React-formuliercomponent

```tsx theme={null}
// resources/js/pages/posts/create.tsx
import { useForm } from '@inertiajs/react'
import { FormEventHandler } from 'react'

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

    const submit: FormEventHandler = (e) => {
        e.preventDefault()
        post('/posts')
    }

    return (
        <form onSubmit={submit}>
            <div>
                <label>Titel</label>
                <input
                    type="text"
                    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 ? 'Versturen...' : 'Posten'}
            </button>
        </form>
    )
}
```

De belangrijkste properties van het object dat `useForm` teruggeeft:

| Property / methode      | Beschrijving                                                           |
| ----------------------- | ---------------------------------------------------------------------- |
| `data`                  | Het dataobject van het formulier                                       |
| `setData(field, value)` | Werkt de waarde van een veld bij                                       |
| `errors`                | Validatiefouten (toegankelijk per veldnaam)                            |
| `processing`            | `true` tijdens het verzenden (gebruik dit om de knop uit te schakelen) |
| `isDirty`               | `true` als er iets is gewijzigd ten opzichte van de beginwaarden       |
| `post(url)`             | Verzenden met een POST-request                                         |
| `put(url)`              | Verzenden met een PUT-request (bijwerken)                              |
| `delete(url)`           | Verzenden met een DELETE-request                                       |
| `reset()`               | Zet het formulier terug naar de beginwaarden                           |

Komt er een validatiefout terug, dan toont `useForm` de fouten terwijl de ingevoerde inhoud behouden blijft.

***

## Gedeelde data (shared data)

Data die je op elke pagina nodig hebt (zoals de ingelogde gebruiker en flashberichten) definieer je in de methode `share()` 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), [
            'auth' => [
                'user' => $request->user()
                    ? $request->user()->only('id', 'name', 'email')
                    : null,
            ],
            'flash' => [
                'success' => $request->session()->get('success'),
                'error'   => $request->session()->get('error'),
            ],
        ]);
    }
}
```

Om vanuit een React-component bij de gedeelde data te komen, gebruik je de `usePage()`-hook.

```tsx theme={null}
import { usePage } from '@inertiajs/react'

type SharedProps = {
    auth: {
        user: { id: number; name: string; email: string } | null
    }
    flash: {
        success: string | null
        error: string | null
    }
}

export default function AppHeader() {
    const { auth, flash } = usePage<SharedProps>().props

    return (
        <>
            <header>
                {auth.user ? (
                    <span>{auth.user.name}</span>
                ) : (
                    <span>Gast</span>
                )}
            </header>

            {flash.success && (
                <div className="alert-success">{flash.success}</div>
            )}
        </>
    )
}
```

<Info>
  Gedeelde data wordt bij elke request meegestuurd; beperk deze daarom tot het strikt noodzakelijke. Gebruik je lazy evaluation met `fn()`, dan wordt de data alleen geëvalueerd wanneer er daadwerkelijk toegang toe is.
</Info>

***

## De basis van React-hooks

Hier zijn de fundamentele React-hooks die je moet kennen om met Inertia × React te ontwikkelen.

### `useState` — lokaal statebeheer

```tsx theme={null}
import { useState } from 'react'

export default function Counter() {
    const [count, setCount] = useState(0)
    const [isOpen, setIsOpen] = useState(false)

    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
        </div>
    )
}
```

### `useEffect` — side effects verwerken

```tsx theme={null}
import { useState, useEffect } from 'react'

export default function Timer() {
    const [seconds, setSeconds] = useState(0)

    useEffect(() => {
        const timer = setInterval(() => {
            setSeconds((s) => s + 1)
        }, 1000)

        // Cleanup-functie
        return () => clearInterval(timer)
    }, []) // Lege array = één keer uitvoeren bij het mounten

    return <p>Verstreken tijd: {seconds} seconden</p>
}
```

### `useMemo` en `useCallback` — prestatieoptimalisatie

```tsx theme={null}
import { useMemo, useCallback } from 'react'
import { router } from '@inertiajs/react'

type Post = { id: number; title: string; published: boolean }
type Props = { posts: Post[] }

export default function PostsList({ posts }: Props) {
    // Waarde memoizen (wordt niet opnieuw berekend zolang posts niet verandert)
    const publishedPosts = useMemo(
        () => posts.filter((post) => post.published),
        [posts],
    )

    // Functie memoizen (wordt niet opnieuw aangemaakt zolang de dependencies niet veranderen)
    const handleClick = useCallback((id: number) => {
        router.visit(`/posts/${id}`)
    }, [])

    return (
        <ul>
            {publishedPosts.map((post) => (
                <li key={post.id} onClick={() => handleClick(post.id)}>
                    {post.title}
                </li>
            ))}
        </ul>
    )
}
```

***

## TypeScript-ondersteuning

De React-versie van de starter kit gebruikt standaard TypeScript. In combinatie met de typedefinities van Inertia waarborg je de typeveiligheid van je props.

### Globale typedefinities

In de starter kits definieer je de types van gedeelde data in `resources/js/types/index.d.ts`.

```ts theme={null}
// resources/js/types/index.d.ts
export interface User {
    id: number
    name: string
    email: string
    email_verified_at?: string
}

export type PageProps<T extends Record<string, unknown> = Record<string, unknown>> = T & {
    auth: {
        user: User
    }
}
```

### Types gebruiken in paginacomponenten

```tsx theme={null}
import { PageProps } from '@/types'

type Post = {
    id: number
    title: string
    content: string
}

export default function PostsIndex({ auth, posts }: PageProps<{ posts: Post[] }>) {
    return (
        <div>
            <p>Ingelogd als: {auth.user.name}</p>
            {posts.map((post) => (
                <article key={post.id}>
                    <h2>{post.title}</h2>
                </article>
            ))}
        </div>
    )
}
```

***

## Samenvatting

React komt in combinatie met Laravel vooral tot zijn recht in de "moderne monoliet"-opzet via Inertia. De uitstekende samenwerking met TypeScript maakt het geschikt voor de ontwikkeling van grootschalige applicaties.

| Element               | Rol                                                      |
| --------------------- | -------------------------------------------------------- |
| Laravel-controller    | Routing, data ophalen, validatie                         |
| `Inertia::render()`   | Geeft data door van de controller aan de React-component |
| React-paginacomponent | Ontvangt props en rendert de UI                          |
| `useForm`             | Statebeheer, verzenden en fouten tonen bij formulieren   |
| `Link`-component      | Paginanavigatie zonder volledige reload                  |
| `usePage().props`     | Toegang tot gedeelde data                                |
| TypeScript            | Typeveilige props en sterkere IDE-autocompletion         |

Met Inertia × React krijg je een ontwikkelervaring die de eenvoud van een Laravel-backend combineert met het krachtige ecosysteem van React. Maak je een project aan met de starter kit, dan kun je direct beginnen met ontwikkelen, inclusief authenticatieschermen.

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


## Related topics

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