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

# React – Einstieg mit Inertia × Laravel

> Einstiegs-Guide zur Verwendung von React mit Laravel + Inertia.js. Historischer Hintergrund von laravel/ui bis zu den aktuellen Starterkits sowie praxisnahe Erläuterung der Inertia-React-Hooks wie useForm und usePage.

## Was ist React?

React ist eine von Meta (früher Facebook) entwickelte und gepflegte JavaScript-Bibliothek zum Aufbau von Benutzeroberflächen. Kennzeichnend sind die deklarative UI-Beschreibung und die **komponentenbasierte** Architektur; React wird für kleine Widgets bis hin zu vollständigen SPAs eingesetzt.

Der Kern von React ist das effiziente Re-Rendering über einen **virtuellen DOM**. Ändert sich der State, überträgt React nur die Differenz ins DOM – Sie müssen das DOM nicht manuell manipulieren.

<Info>
  Diese Seite behandelt die Kombination React 19 mit Inertia v3. Die Starterkits von Laravel 13 verwenden diese Konstellation standardmäßig.
</Info>

### JSX und TSX

React-Komponenten werden in **JSX** (JavaScript XML) geschrieben. Eine HTML-ähnliche Syntax lässt sich direkt in JavaScript einbetten.

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

In den Starterkits ist **TypeScript** (`.tsx`) Standard. Typdefinitionen verbessern die IDE-Vervollständigung und helfen, Bugs früh zu erkennen.

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

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

<Tip>
  Das React-Starterkit von Laravel setzt standardmäßig auf TypeScript + TSX. Alle Beispiele auf dieser Seite sind ebenfalls in TSX geschrieben.
</Tip>

***

## Rolle in Laravel

### Historie

Die Beziehung zwischen React und Laravel ist etwas jünger als die zu Vue, mittlerweile aber mindestens gleichrangig.

```mermaid theme={null}
timeline
    title Laravel und React im Zeitverlauf
    2019 : Laravel 6 – React-Scaffold in laravel/ui ergänzt
    2021 : Laravel 8 – Inertia-React-Stack in Breeze
    2022 : Laravel 9 – Wechsel zu Vite
    2025 : Laravel 12 – Starterkits erneuert (React / Vue)
    2026 : Laravel 13 – Starterkits mit Inertia v3 kompatibel
```

Mit **Laravel 6 (2019)** wurde das Auth-Scaffold in das Paket `laravel/ui` ausgelagert; zusammen mit Vue erschien auch ein React-Scaffold. Zu diesem Zeitpunkt dominierte Vue, und die React-Variante fristete ein Nischendasein.

In **Laravel Breeze (2021)** wurde der Inertia + React-Stack ergänzt und damit die ernsthafte Adoption gestartet. Mit der Starterkit-Erneuerung in **Laravel 12 (2025)** wurde React vollständig gleichrangig zu Vue (und wird sogar zuerst angezeigt).

### Aktueller Standard: Inertia × React

Der Standard-Ansatz für React in Laravel ist heute **Inertia × React**. Inertia ermöglicht die „moderne Monolith"-Architektur, bei der Sie ohne API-Design direkt aus dem Laravel-Controller Daten an React-Komponenten übergeben.

```mermaid theme={null}
graph LR
    Browser["Browser"]
    Inertia["Inertia.js<br>(Adapter-Schicht)"]
    Laravel["Laravel<br>(Controller)"]
    React["React<br>(Page-Komponente)"]

    Browser <-->|XHR / Full-Page-Load| Inertia
    Inertia <-->|Inertia-Response| Laravel
    Inertia -->|props| React
    React -->|Rendering| Browser
```

***

## Setup

### Via Starterkit (empfohlen)

Beim Aufsetzen eines neuen Projekts ist das Starterkit am komfortabelsten.

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

Wenn Sie im interaktiven Prompt **React** wählen, wird automatisch alles Folgende eingerichtet:

* `inertiajs/inertia-laravel` (serverseitiger Adapter)
* `@inertiajs/react` (Client-Adapter)
* `react` + `react-dom` (React 19 selbst)
* `@vitejs/plugin-react` (Vite-Plugin)
* TypeScript + `@types/react`
* Tailwind CSS + shadcn/ui-Komponenten
* Middleware `HandleInertiaRequests`
* Auth-Seiten für Login, Registrierung u. a. (bereits mit Inertia + React + TypeScript umgesetzt)

### Manuelle Installation

Für eine Ergänzung in einem bestehenden Projekt installieren Sie Server- und Client-Seite separat.

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

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

Ergänzen Sie anschließend das React-Plugin in `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(),
    ],
})
```

Starten Sie die 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>
  Details zur manuellen Installation (Root-Template, Middleware-Registrierung usw.) finden Sie in der [offiziellen Inertia-Dokumentation](https://inertiajs.com/installation).
</Info>

***

## Verzeichnisstruktur

Im Starterkit werden React-Page-Komponenten unter `resources/js/pages/` abgelegt.

```
resources/js/
├── app.tsx            # Einstiegspunkt der Inertia-App
├── bootstrap.ts
├── components/        # Wiederverwendbare UI-Komponenten
│   ├── ui/            # shadcn/ui-Komponenten
│   └── ...
├── hooks/             # Benutzerdefinierte React-Hooks
├── layouts/           # Layout-Komponenten
│   ├── app-layout.tsx
│   └── auth-layout.tsx
├── lib/               # Utility-Funktionen und Konfiguration
├── pages/             # Inertia-Page-Komponenten (analog zu Controller-Namen)
│   ├── auth/
│   │   ├── login.tsx
│   │   └── register.tsx
│   ├── dashboard.tsx
│   └── posts/
│       ├── index.tsx
│       ├── create.tsx
│       └── show.tsx
└── types/             # TypeScript-Typdefinitionen
```

`Inertia::render('posts/index', [...])` verweist auf die Komponente `resources/js/pages/posts/index.tsx`.

***

## Grundlagen der JSX-Syntax

React verwendet JSX. Nachfolgend die minimal notwendigen Muster, um den Code der Starterkits lesen zu können.

#### `{}` – Variablen-Interpolation

Innerhalb von JSX betten Sie JavaScript-Werte und -Ausdrücke mit `{}` ein.

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

return (
    <div>
        <p>Hallo, {name}!</p>
        <p>Doppelter Wert: {count * 2}</p>
    </div>
)
```

#### Bedingte Darstellung – `&&` und ternärer Operator

React hat keine Direktive wie `v-if`. Für einfache Bedingungen nutzen Sie den `&&`-Operator, für if/else den ternären Operator `? :`.

```tsx theme={null}
{/* Nur anzeigen, wenn Bedingung true */}
{isLoggedIn && <p>Willkommen!</p>}

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

{/* if / else if / else */}
{isLoggedIn
    ? <p>Willkommen!</p>
    : role === 'admin'
        ? <p>Als Administrator angemeldet</p>
        : <a href="/login">Anmelden</a>
}
```

#### Listen darstellen – `.map()`

Für Listen wird `Array.map()` verwendet. Für ein effizientes diffbasiertes Rendering ist das `key`-Prop erforderlich.

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

Entspricht `v-for :key` in Vue oder `{#each}` in Svelte.

#### `className` – CSS-Klassennamen

Da JSX zu JavaScript kompiliert wird und `class` ein reserviertes Wort ist, verwenden Sie für CSS-Klassen `className`.

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

#### Event-Handler – camelCase

Event-Attribute in JSX werden in camelCase geschrieben; Sie übergeben eine Funktionsreferenz.

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

{/* Default-Verhalten des Formular-Submits verhindern */}
<form onSubmit={(e) => { e.preventDefault(); handleSubmit() }}>
    ...
</form>
```

***

## Grundlagen der Page-Komponente

Eine Inertia-Page-Komponente ist eine gewöhnliche React-Komponente. Die aus dem Laravel-Controller übergebenen Daten stehen als Props zur Verfügung.

### 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-Page-Komponente

```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) liefert ein Objekt mit Pagination-Infos
        // current_page, last_page, per_page, total sind ebenfalls verfügbar
    }
}

export default function PostsIndex({ posts }: Props) {
    return (
        <div>
            <h1>Beitragsübersicht</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>
    )
}
```

Sie empfangen die Props als Argument der Komponente und verwenden die vom Controller übergebenen Daten direkt. Eine REST-API muss nicht definiert werden.

***

## Die `Link`-Komponente

Die von `@inertiajs/react` bereitgestellte `<Link>`-Komponente sorgt dafür, dass Seitenwechsel per XHR ablaufen und ein Full-Page-Reload vermieden wird.

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

export default function PostsIndex() {
    return (
        <div>
            {/* Einfacher Link */}
            <Link href="/posts">Beitragsübersicht</Link>

            {/* Link mit POST-Methode (z. B. Löschen) */}
            <Link href="/posts/1" method="delete" as="button">
                Löschen
            </Link>

            {/* Preload (bei Hover vorab laden) */}
            <Link href="/posts/1" preload>
                Beitrag ansehen
            </Link>
        </div>
    )
}
```

Sie schreiben wie eine gewöhnliche `<a>`-Anweisung. Im Hintergrund tauscht Inertia nur die Page-Komponente aus – so entsteht das SPA-artige Gefühl.

***

## Die `Form`-Komponente

Die `<Form>`-Komponente von `@inertiajs/react` ist der empfohlene Stil zum Absenden von Formularen im Starterkit. Sie geben `action` und `method` als Props an und erhalten `errors` und `processing` über eine Children-Funktion (Render-Prop).

### Grundlegende Verwendung

```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">Inhalt</label>
                        <textarea id="content" name="content" />
                        {errors.content && <p className="error">{errors.content}</p>}
                    </div>

                    <button type="submit" disabled={processing}>
                        {processing ? 'Wird gesendet...' : 'Veröffentlichen'}
                    </button>
                </>
            )}
        </Form>
    )
}
```

Das Children von `<Form>` ist eine Funktion `({ errors, processing }) => JSX` (Render-Prop-Muster). Die `Form`-Komponente berechnet diese Werte automatisch und übergibt sie. Formularfelder verwenden statt eines `onChange`-Handlers das HTML-native `name`-Attribut, sodass die eingebaute Formularerfassung des Browsers funktioniert.

### Muster aus dem Starterkit

Die Starterkits verwalten Routen mit [Wayfinder](/de/blog/wayfinder-introduction) als Objekte. `store.form()` gibt ein Objekt mit `action` und `method` aus dem Route-Objekt zurück, das per Spread an `<Form>` übergeben wird.

```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 }) => (
                <>
                    {/* Formularinhalt */}
                </>
            )}
        </Form>
    )
}
```

Die in `resetOnSuccess` angegebenen Felder werden bei erfolgreichem Absenden automatisch zurückgesetzt. Nutzen Sie das etwa für Passwortfelder, die nach dem Absenden geleert werden sollen.

<Info>
  Ohne Wayfinder verhalten sich `action="/login"` und Co. identisch, sobald Sie die URL direkt übergeben.
</Info>

***

## Der `useForm`-Hook

Für die Formularverarbeitung nutzen Sie den `useForm`-Hook aus `@inertiajs/react`. State-Handling, Absenden und Anzeige von Validierungsfehlern lassen sich damit schlank umsetzen.

### Auf der Controller-Seite

```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', 'Der Beitrag wurde erstellt.');
    }
}
```

### React-Formular-Komponente

```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>Inhalt</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 ? 'Wird gesendet...' : 'Veröffentlichen'}
            </button>
        </form>
    )
}
```

Wichtige Eigenschaften des von `useForm` zurückgegebenen Objekts:

| Eigenschaft / Methode   | Beschreibung                                                          |
| ----------------------- | --------------------------------------------------------------------- |
| `data`                  | Datenobjekt des Formulars                                             |
| `setData(field, value)` | Wert eines Feldes aktualisieren                                       |
| `errors`                | Validierungsfehler (Zugriff per Feldname)                             |
| `processing`            | `true`, solange das Formular gesendet wird (für Button-Deaktivierung) |
| `isDirty`               | `true`, wenn sich der Wert vom Ursprung unterscheidet                 |
| `post(url)`             | Per POST senden                                                       |
| `put(url)`              | Per PUT senden (Update)                                               |
| `delete(url)`           | Per DELETE senden                                                     |
| `reset()`               | Formular auf Initialwerte zurücksetzen                                |

Bei Validierungsfehlern behält `useForm` die Eingaben und zeigt die Fehler an.

***

## Shared Data

Daten, die auf allen Seiten gemeinsam nötig sind (angemeldeter Nutzer, Flash-Messages u. a.), definieren Sie in der Methode `share()` der 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'),
            ],
        ]);
    }
}
```

Für den Zugriff aus einer React-Komponente verwenden Sie den `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>
  Da Shared Data in jeder Anfrage enthalten sind, sollten Sie sie auf das Nötigste beschränken. Eine Lazy-Evaluation per `fn()` wird nur ausgeführt, wenn die Daten tatsächlich gelesen werden.
</Info>

***

## Grundlagen der React-Hooks

Wichtige React-Hooks für die Arbeit mit Inertia × React:

### `useState` – lokaler State

```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` – Seiteneffekte

```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-Funktion
        return () => clearInterval(timer)
    }, []) // Leeres Array = nur einmal beim Mount
    return <p>Verstrichene Zeit: {seconds} s</p>
}
```

### `useMemo` und `useCallback` – Performance-Optimierung

```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) {
    // Wert memoisieren (nur neu berechnen, wenn sich posts ändern)
    const publishedPosts = useMemo(
        () => posts.filter((post) => post.published),
        [posts],
    )

    // Funktion memoisieren (nur neu erzeugen, wenn sich Abhängigkeiten ändern)
    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-Unterstützung

Die React-Variante der Starterkits nutzt standardmäßig TypeScript. In Kombination mit den Typdefinitionen von Inertia erhalten Sie Typsicherheit für Props.

### Globale Typdefinitionen

In den Starterkits werden die Typen der Shared Data in `resources/js/types/index.d.ts` definiert.

```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
    }
}
```

### Verwendung der Typen in Page-Komponenten

```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>Angemeldet: {auth.user.name}</p>
            {posts.map((post) => (
                <article key={post.id}>
                    <h2>{post.title}</h2>
                </article>
            ))}
        </div>
    )
}
```

***

## Fazit

React entfaltet in Kombination mit Laravel – insbesondere über Inertia im Sinne eines „modernen Monolithen" – seine Stärken. Die Zusammenarbeit mit TypeScript ist ausgezeichnet, was React besonders für große Anwendungen prädestiniert.

| Baustein              | Rolle                                                  |
| --------------------- | ------------------------------------------------------ |
| Laravel-Controller    | Routing, Datenerfassung, Validierung                   |
| `Inertia::render()`   | Daten vom Controller an die React-Komponente reichen   |
| React-Page-Komponente | Props empfangen und UI rendern                         |
| `useForm`             | Formular-State, Absenden und Fehleranzeige             |
| `Link`-Komponente     | Seitenwechsel ohne Full-Reload                         |
| `usePage().props`     | Zugriff auf Shared Data                                |
| TypeScript            | Typsicherheit der Props und bessere IDE-Autocompletion |

Mit Inertia × React verbinden Sie die Einfachheit des Laravel-Backends mit dem starken Ökosystem von React. Legen Sie ein Projekt mit dem Starterkit an, sind auch die Auth-Screens sofort einsatzbereit.

<Card title="Offizielle Inertia.js-Dokumentation" icon="book-open" href="https://inertiajs.com">
  Alle Funktionen von Inertia v3 finden Sie in der offiziellen Dokumentation.
</Card>


## Related topics

- [Svelte – Einstieg mit Inertia × Laravel](/de/blog/svelte-introduction.md)
- [Vue.js – Einstieg mit Inertia × Laravel](/de/blog/vue-introduction.md)
- [Frontend](/de/frontend.md)
- [Wissen, das Sie vor dem Einstieg in Laravel benötigen](/de/true-tutorial.md)
- [SPAs mit Inertia.js entwickeln](/de/blog/inertia-introduction.md)
