Eine Komponente bekommt Daten von ihrem Eltern-Komponent über Properties — kurz Props. In Svelte 5 holst du sie mit $props(). Statt für jede Prop ein eigenes export let-Statement zu schreiben (wie in Svelte 4), destrukturierst du das gesamte Eingabe-Objekt in einer einzigen Zeile. Defaults, Rest-Operator und TypeScript-Typen passen in dieselbe Schreibweise.

Grundprinzip

svelte Greeting.svelte
<script>
    let { name, age } = $props();
</script>

<p>{name}, {age} Jahre</p>

$props() gibt ein Objekt mit allen vom Eltern-Komponent übergebenen Werten zurück. Du destrukturierst es wie jedes andere Objekt in JavaScript — die linke Seite zählt auf, was du verwenden willst.

So sieht der Aufruf aus Sicht des Eltern-Komponents aus:

svelte Aufruf
<Greeting name="Anna" age={30} />

Die Werte landen unter den gleichen Namen, die du beim Destrukturieren angibst.

Defaults setzen

Wie bei normaler Destrukturierung: einfach ein Default-Wert nach =:

svelte Defaults
<script>
    let { name = 'Welt', greeting = 'Hallo' } = $props();
</script>

<p>{greeting}, {name}!</p>

Wird kein Wert übergeben, greift der Default.

Rest-Props weiterreichen

Bei Wrapper-Komponenten will man oft alle nicht-konsumierten Props an ein inneres Element durchreichen:

svelte StyledButton.svelte
<script>
    let { variant = 'default', children, ...rest } = $props();
</script>

<button class={`btn btn-${variant}`} {...rest}>
    {@render children()}
</button>
svelte Verwendung
<StyledButton variant="primary" onclick={save} disabled={loading}>
    Speichern
</StyledButton>

onclick und disabled werden über den Spread {...rest} direkt am <button> gesetzt.

TypeScript: Props typisieren

Mit lang="ts" lassen sich Props sauber typisieren:

svelte Greeting.svelte (TypeScript)
<script lang="ts">
    type Props = {
        name?: string;
        age: number;
        onSave?: (name: string) => void;
    };

    let { name = 'Welt', age, onSave }: Props = $props();
</script>

<p>{name}, {age} Jahre</p>
<button onclick={() => onSave?.(name)}>Speichern</button>

Die Type-Annotation steht nach der Destrukturierung. Optional/required wird im Type ausgedrückt.

Children typisieren

Snippets sind Funktionen, die JSX-ähnliche Inhalte zurückgeben — der Typ heißt Snippet:

svelte Card.svelte mit Snippet-Type
<script lang="ts">
    import type { Snippet } from 'svelte';

    type Props = {
        children: Snippet;
        header?: Snippet;
    };

    let { children, header }: Props = $props();
</script>

<article>
    {#if header}
        <header>{@render header()}</header>
    {/if}
    <div class="body">
        {@render children()}
    </div>
</article>

Snippets können auch Argumente nehmen — siehe Snippets statt Slots.

Reservierte Property-Namen umbenennen

JavaScript-Schlüsselwörter wie class oder for lassen sich nicht direkt destrukturieren. Mit : wird umbenannt:

svelte class umbenennen
<script>
    let { class: className, for: htmlFor, ...rest } = $props();
</script>

<label class={className} {htmlFor}>...</label>

Props sind read-only

Du darfst eine Prop nicht direkt überschreiben:

ts − Verboten
let { count } = $props();
count = 10; // Compiler-Warnung in Svelte 5

Soll der Wert sich ändern können, gibt es zwei Wege:

  1. Initialer Wert kopieren in eigenen $state:

    ts Lokale Kopie
    import { untrack } from 'svelte';
    
    let { initial = 0 } = $props();
    let count = $state(untrack(() => initial));

    Warum untrack? initial ist eine reaktive Prop. Ohne untrack warnt der Compiler, dass count nur den ersten Wert übernimmt und danach nicht mehr mit initial synchron bleibt. untrack macht diese gewollte Entkopplung explizit und stellt die Warnung still. Wenn count doch automatisch synchron bleiben soll, ist Variante 2 ($bindable) oder ein $effect die richtige Wahl.

  2. Two-Way-Binding mit $bindable — siehe Artikel $bindable.

Migration von Svelte 4

Svelte 4Svelte 5
export let name;let { name } = $props();
export let name = 'Welt';let { name = 'Welt' } = $props();
export let class as className;let { class: className } = $props();
$$props, $$restPropslet { ...rest } = $props();
<slot />let { children } = $props(); {@render children()}
<slot name="header">let { header } = $props(); {@render header?.()}

Häufige Stolperfallen

Fehlende Destrukturierung. let props = $props(); console.log(props.name); funktioniert technisch, aber Reaktivität wird subtil. Die destrukturierte Form ist idiomatisch und wird vom Compiler optimal behandelt.

Defaults für required-Props. Wenn TypeScript die Prop als required markiert, sollte kein Default gesetzt werden — das verschleiert API-Erwartungen.

Mutation der Prop-Werte. Auch bei Objekt-Props: Eine empfangene Prop nicht direkt mutieren. Stattdessen lokal in eigenen State spiegeln oder mit $bindable arbeiten.

Snippet-Aufruf vergessen. {@render children()} muss explizit aufgerufen werden — Snippets rendern sich nicht automatisch.

Spread-Reihenfolge bei Klassen.

svelte Reihenfolge beachten
<!-- Wenn Props nach class kommen, überschreiben sie eigene Klassen -->
<button class="btn" {...rest} />

<!-- Eigene Klasse gewinnt -->
<button {...rest} class="btn" />

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Reactivity (Runes)

Zur Übersicht