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

# Svelte – Einstieg mit Inertia × Laravel

> Einstiegs-Guide zur Nutzung von Svelte mit Laravel + Inertia.js. Vom Compiler-basierten Ansatz und der Runen-Syntax von Svelte 5 bis zu Inertia-Svelte-Hooks wie useForm und usePage – praxisorientiert erklärt.

## Was ist Svelte?

[Svelte](https://svelte.dev/) nimmt unter den JavaScript-Frameworks eine besondere Position ein. Während React und Vue als **Laufzeitbibliotheken** arbeiten, wirkt Svelte als **Compiler**. Beim Build werden Komponenten in reines JavaScript umgewandelt, sodass keine zusätzliche Framework-Runtime an den Browser gesendet werden muss.

Das prägnanteste Merkmal von Svelte ist der **Verzicht auf einen Virtual DOM**. Bei Zustandsänderungen aktualisiert der von Svelte zur Compile-Zeit erzeugte Code das DOM direkt. Dadurch entstehen sehr leichte, schnelle Oberflächen.

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

### Runen in Svelte 5

Mit Svelte 5 (Release 2024) wurde ein neues Reaktivitätssystem eingeführt: die **Runen (Runes)**. Reaktiver State wird mit besonderen Funktionen (Runen) wie `$state`, `$derived` und `$effect` deklariert. Anders als die implizite Reaktivität in Svelte 4 ist das Design explizit und leichter vorhersagbar.

```svelte theme={null}
<script lang="ts">
    let count = $state(0)

    function increment() {
        count++
    }
</script>

<button onclick={increment}>{count}</button>
```

<Tip>
  Das Svelte-Starterkit von Laravel setzt standardmäßig auf Svelte 5 mit TypeScript. Alle Beispiele auf dieser Seite sind ebenfalls in TypeScript (`lang="ts"`) geschrieben.
</Tip>

***

## Rolle in Laravel

### Historie

Die Beziehung zwischen Svelte und Laravel ist gegenüber Vue und React sehr jung – der erste offizielle Support ist die Aufnahme in das **offizielle Starterkit**.

```mermaid theme={null}
timeline
    title Laravel und Svelte im Zeitverlauf
    2016 : Svelte 1 (von Rich Harris)
    2019 : Svelte 3 – Compiler-basiertes Redesign
    2021 : Inertia.js – Community stellt Svelte-Adapter bereit
    2024 : Svelte 5 – Einführung der Runen-Syntax
    2026 : Laravel 13 – Svelte-Starterkit offiziell ergänzt (Inertia v3-fähig)
```

Mit **Laravel 13 (2026)** wurde Svelte offiziell den Starterkits hinzugefügt und ist damit eine offizielle Frontend-Option im Laravel-Ökosystem. Es steht gleichrangig neben Vue und React und lässt sich im interaktiven Prompt von `laravel new` auswählen.

Für viele Laravel-Nutzer ist Svelte noch weniger bekannt; kennzeichnend sind der **kleine Bundle-Footprint durch den Compiler** und die **schlanke Syntax**. Wer von Vue oder React umsteigt, ist häufig überrascht, wie wenig Code nötig ist.

### Aktueller Standard: Inertia × Svelte

Der Standardansatz für Svelte in Laravel ist **Inertia × Svelte**. Inertia realisiert die „moderne Monolith"-Architektur, bei der Daten aus dem Laravel-Controller direkt an Svelte-Komponenten gehen, ohne API-Design.

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

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

***

## Setup

### Via Starterkit (empfohlen)

Für neue Projekte ist das Starterkit am komfortabelsten.

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

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

* `inertiajs/inertia-laravel` (serverseitiger Adapter)
* `@inertiajs/svelte` (Client-Adapter)
* `svelte` + `@sveltejs/vite-plugin-svelte` (Svelte 5 selbst und Vite-Plugin)
* TypeScript + `svelte-check`
* Tailwind CSS + Komponentenbibliothek shadcn-svelte
* Middleware `HandleInertiaRequests`
* Auth-Seiten für Login, Registrierung u. a. (bereits mit Inertia + Svelte + TypeScript umgesetzt)

### Manuelle Installation

Für die 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/svelte @inertiajs/vite svelte
npm install --save-dev @sveltejs/vite-plugin-svelte svelte-check typescript
```

Ergänzen Sie anschließend in `vite.config.ts` das Svelte-Plugin und das Inertia-Vite-Plugin:

```ts theme={null}
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import inertia from '@inertiajs/vite'

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

Starten Sie die Inertia-App in `resources/js/app.ts`. Da das Plugin `@inertiajs/vite` die automatische Auflösung und das Mounting übernimmt, genügt ein minimaler Entry-Point.

```ts theme={null}
import { createInertiaApp } from '@inertiajs/svelte'

createInertiaApp()
```

<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 Svelte-Page-Komponenten unter `resources/js/pages/` abgelegt.

```
resources/js/
├── app.ts             # Einstiegspunkt der Inertia-App
├── components/        # Wiederverwendbare UI-Komponenten
│   └── ui/            # shadcn-svelte-Komponenten
├── layouts/           # Layout-Komponenten
│   ├── AppLayout.svelte
│   └── AuthLayout.svelte
├── lib/               # Utility-Funktionen und Svelte-Runen-Module
├── pages/             # Inertia-Page-Komponenten (analog zu Controller-Namen)
│   ├── auth/
│   │   ├── Login.svelte
│   │   └── Register.svelte
│   ├── Dashboard.svelte
│   └── posts/
│       ├── Index.svelte
│       ├── Create.svelte
│       └── Show.svelte
└── types/             # TypeScript-Typdefinitionen
```

`Inertia::render('posts/Index', [...])` verweist auf `resources/js/pages/posts/Index.svelte`.

***

## Grundaufbau einer Svelte-Datei

Eine `.svelte`-Datei besteht aus drei Blöcken: **`<script>`, Template und `<style>`**.

```svelte theme={null}
<script lang="ts">
    // Logik (TypeScript)
    let name = $state('Laravel')
</script>

<!-- Template (HTML-ähnliche Notation) -->
<h1>Hello, {name}!</h1>

<style>
    /* Scoped CSS (nur auf diese Komponente angewendet) */
    h1 {
        color: #ff2d20;
    }
</style>
```

Ähnlich wie die Single-File-Components (SFC) von Vue, allerdings mit weniger Boilerplate in Template und Script. `<style>` ist standardmäßig gescoped, sodass Sie sich um Klassennamen-Kollisionen nicht sorgen müssen.

### Template-Syntax

#### Variablen und Ausdrücke

Innerhalb des Templates betten Sie JavaScript-Werte mit `{}` ein.

```svelte theme={null}
<script lang="ts">
    let name = $state('Welt')
    let count = $state(3)
</script>

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

#### `{#if}` – Bedingte Anzeige

```svelte theme={null}
<script lang="ts">
    let isLoggedIn = $state(false)
    let role = $state('editor')
</script>

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

Entspricht `v-if` / `v-else` in Vue und dem ternären Operator in React.

#### `{#each}` – Listen darstellen

```svelte theme={null}
<script lang="ts">
    type Post = { id: number; title: string }
    let posts = $state<Post[]>([
        { id: 1, title: 'Erster Beitrag' },
        { id: 2, title: 'Zweiter Beitrag' },
    ])
</script>

<ul>
    {#each posts as post (post.id)}
        <li>{post.title}</li>
    {/each}
</ul>
```

`(post.id)` ist die Key-Angabe und wird für effizientes Diffing genutzt. Entspricht `v-for` in Vue und `Array.map()` in React.

#### `bind:` – Zwei-Wege-Binding

Mit `bind:value` synchronisieren Sie den Wert eines Formularelements bidirektional mit einer reaktiven Variablen.

```svelte theme={null}
<script lang="ts">
    let title = $state('')
    let agreed = $state(false)
    let role = $state('viewer')
</script>

<!-- Text-Input -->
<input bind:value={title} type="text" />
<p>Eingabe: {title}</p>

<!-- Checkbox -->
<input bind:checked={agreed} type="checkbox" />
<p>Zustimmung: {agreed}</p>

<!-- Select -->
<select bind:value={role}>
    <option value="viewer">Betrachter</option>
    <option value="editor">Bearbeiter</option>
    <option value="admin">Administrator</option>
</select>
```

Entspricht `v-model` in Vue. Anders als bei React, wo `onChange`-Handler nötig sind, drücken Sie es in Svelte deklarativ mit `bind:` aus.

***

## Grundlagen der Page-Komponente

Eine Inertia-Page-Komponente ist eine gewöhnliche Svelte-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),
        ]);
    }
}
```

### Svelte-Page-Komponente

In Svelte 5 empfangen Sie Props mit der Rune `$props()`.

```svelte theme={null}
<!-- resources/js/pages/posts/Index.svelte -->
<script lang="ts">
    import { Link } from '@inertiajs/svelte'

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

    type Props = {
        posts: {
            data: Post[]
        }
    }

    let { posts }: Props = $props()
</script>

<div>
    <h1>Beitragsübersicht</h1>
    {#each posts.data as post (post.id)}
        <article>
            <h2>
                <Link href={`/posts/${post.id}`}>{post.title}</Link>
            </h2>
            <p>{post.created_at}</p>
        </article>
    {/each}
</div>
```

Nach `$props()` verwenden Sie die vom Controller übergebenen Daten direkt im Template. Eine REST-API ist nicht nötig.

***

## Die `Link`-Komponente

Die von `@inertiajs/svelte` bereitgestellte `<Link>`-Komponente sorgt für Seitenwechsel per XHR und vermeidet ein Full-Page-Reload.

```svelte theme={null}
<script lang="ts">
    import { Link } from '@inertiajs/svelte'
</script>

<!-- Einfacher Link -->
<Link href="/posts">Beitragsübersicht</Link>

<!-- Link per POST (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>
```

Die Schreibweise gleicht einem `<a>`-Tag; im Hintergrund tauscht Inertia nur die Page-Komponente aus.

***

## Die `Form`-Komponente

Die `<Form>`-Komponente von `@inertiajs/svelte` ist der empfohlene Stil aus den Starterkit-Auth-Screens. Sie geben `action` und `method` als Props an und beschreiben das Innere mit **`{#snippet}`**.

### Grundlegende Verwendung

```svelte theme={null}
<script lang="ts">
    import { Form } from '@inertiajs/svelte'
</script>

<Form action="/posts" method="post" class="flex flex-col gap-4">
    {#snippet children({ errors, processing })}
        <div>
            <label for="title">Titel</label>
            <input id="title" name="title" type="text" required />
            {#if errors.title}
                <p class="error">{errors.title}</p>
            {/if}
        </div>

        <div>
            <label for="content">Inhalt</label>
            <textarea id="content" name="content"></textarea>
            {#if errors.content}
                <p class="error">{errors.content}</p>
            {/if}
        </div>

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

`{#snippet children({ errors, processing })}` ist die Snippet-Syntax von Svelte – ein Content-Block, der an die Komponente übergeben wird (analog zu Vue-Slots oder React-Render-Props). `errors` und `processing` werden von `Form` automatisch berechnet und übergeben. Statt `bind:value` verwenden Sie das native HTML-`name`-Attribut, sodass die Formulardatenerfassung des Browsers zum Zug kommt.

### Muster aus dem Starterkit

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

```svelte theme={null}
<script lang="ts">
    import { Form } from '@inertiajs/svelte'
    import { store } from '@/routes/login'
</script>

<Form
    {...store.form()}
    resetOnSuccess={['password']}
    class="flex flex-col gap-6"
>
    {#snippet children({ errors, processing })}
        <!-- Formularinhalt -->
    {/snippet}
</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/svelte`. State-Handling, Absenden und Fehleranzeige 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.');
    }
}
```

### Svelte-Formular-Komponente

```svelte theme={null}
<!-- resources/js/pages/posts/Create.svelte -->
<script lang="ts">
    import { useForm } from '@inertiajs/svelte'

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

    function submit(e: SubmitEvent) {
        e.preventDefault()
        form.post('/posts')
    }
</script>

<form onsubmit={submit}>
    <div>
        <label>Titel</label>
        <input type="text" bind:value={form.data.title} />
        {#if form.errors.title}
            <p class="error">{form.errors.title}</p>
        {/if}
    </div>

    <div>
        <label>Inhalt</label>
        <textarea bind:value={form.data.content}></textarea>
        {#if form.errors.content}
            <p class="error">{form.errors.content}</p>
        {/if}
    </div>

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

Wichtige Eigenschaften des Rückgabewerts von `useForm`:

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

Bei Validierungsfehlern behält `useForm` die Eingaben und zeigt die Fehler an. In Verbindung mit `bind:value` entsteht ein nahtloses Formularerlebnis.

***

## Shared Data

Daten, die auf allen Seiten gemeinsam gebraucht werden (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'),
            ],
        ]);
    }
}
```

In der Svelte-Komponente greifen Sie mit `usePage()` auf Shared Data zu.

```svelte theme={null}
<script lang="ts">
    import { usePage } from '@inertiajs/svelte'

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

    const page = usePage<SharedProps>()
</script>

<header>
    {#if page.props.auth.user}
        <span>{page.props.auth.user.name}</span>
    {:else}
        <span>Gast</span>
    {/if}
</header>

{#if page.props.flash.success}
    <div class="alert-success">{page.props.flash.success}</div>
{/if}
```

<Info>
  Da Shared Data in jeder Anfrage enthalten sind, empfiehlt sich die Beschränkung auf das Nötigste. Eine Lazy-Evaluation per `fn()` wird nur ausgewertet, wenn tatsächlich zugegriffen wird.
</Info>

***

## Reaktivität in Svelte 5 (Runen)

Wichtige Runen-Konzepte für die Arbeit mit Inertia × Svelte:

### `$state` – reaktiver State

```svelte theme={null}
<script lang="ts">
    let count = $state(0)
    let isOpen = $state(false)
    let items = $state<string[]>([])
</script>

<p>{count}</p>
<button onclick={() => count++}>+1</button>
<button onclick={() => isOpen = !isOpen}>Toggle</button>
```

Mit `$state` deklarierte Variablen sind automatisch reaktiv. Bei Wertänderung wird das DOM aktualisiert. Entspricht Vues `ref` bzw. Reacts `useState`, jedoch ohne `.value`-Zugriff – die Zuweisung reicht.

### `$derived` – abgeleitete Werte

```svelte theme={null}
<script lang="ts">
    let posts = $state<{ title: string; published: boolean }[]>([])

    // Nur Beiträge mit published = true
    let publishedPosts = $derived(posts.filter(post => post.published))

    // Mehrere Abhängigkeiten
    let summary = $derived.by(() => {
        const total = posts.length
        const published = publishedPosts.length
        return `${published} von ${total} veröffentlicht`
    })
</script>

<p>{summary}</p>
```

`$derived` berechnet sich neu, sobald sich abhängige Werte ändern. Entspricht `computed` in Vue und `useMemo` in React.

### `$effect` – Seiteneffekte

```svelte theme={null}
<script lang="ts">
    let query = $state('')

    // Wird bei jeder Änderung von query ausgeführt
    $effect(() => {
        console.log('Suchanfrage geändert:', query)

        // Optional: Cleanup-Funktion zurückgeben
        return () => {
            console.log('Cleanup')
        }
    })
</script>

<input bind:value={query} placeholder="Suchen..." />
```

`$effect` wird bei jeder Änderung eines abhängigen `$state`-Werts ausgeführt. Entspricht `useEffect` in React, aber ohne explizites Dependency-Array – verwendete `$state`-Variablen werden automatisch getrackt.

***

## shadcn-svelte-Komponenten

Das Starterkit enthält [shadcn-svelte](https://www.shadcn-svelte.com/). shadcn-svelte folgt dem gleichen Designprinzip wie shadcn/ui für React: Sie kopieren den Code in Ihr Projekt und passen ihn frei an.

### Komponente hinzufügen

```shell theme={null}
npx shadcn-svelte@latest add button
npx shadcn-svelte@latest add input
npx shadcn-svelte@latest add card
```

Nach dem Befehl liegt der Quellcode der Komponenten in `resources/js/components/ui/`.

### Verwendung

```svelte theme={null}
<script lang="ts">
    import { Button } from '@/components/ui/button'
    import { Input } from '@/components/ui/input'
    import * as Card from '@/components/ui/card'
    import { useForm } from '@inertiajs/svelte'

    const form = useForm({ email: '', password: '' })

    function submit(e: SubmitEvent) {
        e.preventDefault()
        form.post('/login')
    }
</script>

<Card.Root class="w-96">
    <Card.Header>
        <Card.Title>Anmelden</Card.Title>
    </Card.Header>
    <Card.Content>
        <form onsubmit={submit} class="space-y-4">
            <Input
                type="email"
                bind:value={form.data.email}
                placeholder="E-Mail-Adresse"
            />
            {#if form.errors.email}
                <p class="text-sm text-red-500">{form.errors.email}</p>
            {/if}

            <Input
                type="password"
                bind:value={form.data.password}
                placeholder="Passwort"
            />

            <Button type="submit" disabled={form.processing} class="w-full">
                {form.processing ? 'Anmeldung...' : 'Anmelden'}
            </Button>
        </form>
    </Card.Content>
</Card.Root>
```

Im Starterkit sind bereits gängige Komponenten wie Button, Input, Card, Dialog und Dropdown enthalten. Weitere Komponenten ergänzen Sie bei Bedarf mit `npx shadcn-svelte@latest add`.

***

## Fazit

Svelte entfaltet in Kombination mit Laravel – insbesondere über Inertia im Sinne eines „modernen Monolithen" – seine Stärken. Dank der Compiler-Basis ist die Bundle-Größe klein; die Runen-Syntax macht Reaktivität explizit und verständlich.

| Baustein               | Rolle                                                 |
| ---------------------- | ----------------------------------------------------- |
| Laravel-Controller     | Routing, Datenerfassung, Validierung                  |
| `Inertia::render()`    | Daten vom Controller an die Svelte-Komponente reichen |
| Svelte-Page-Komponente | Props per `$props()` empfangen und UI rendern         |
| `useForm`              | Formular-State, Absenden und Fehleranzeige            |
| `Link`-Komponente      | Seitenwechsel ohne Full-Reload                        |
| `usePage().props`      | Zugriff auf Shared Data                               |
| shadcn-svelte          | Standard-Komponentenbibliothek                        |

Mit Inertia × Svelte verbinden Sie die Einfachheit des Laravel-Backends mit der kompakten Ausdrucksweise von Svelte. 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

- [React – Einstieg mit Inertia × Laravel](/de/blog/react-introduction.md)
- [Vue.js – Einstieg mit Inertia × Laravel](/de/blog/vue-introduction.md)
- [Frontend](/de/frontend.md)
- [SPAs mit Inertia.js entwickeln](/de/blog/inertia-introduction.md)
- [Einstieg in Tests](/de/testing.md)
