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
<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:
<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 =:
<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:
<script>
let { variant = 'default', children, ...rest } = $props();
</script>
<button class={`btn btn-${variant}`} {...rest}>
{@render children()}
</button><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:
<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:
<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:
<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:
let { count } = $props();
count = 10; // Compiler-Warnung in Svelte 5Soll der Wert sich ändern können, gibt es zwei Wege:
-
Initialer Wert kopieren in eigenen
$state:ts Lokale Kopie import { untrack } from 'svelte'; let { initial = 0 } = $props(); let count = $state(untrack(() => initial));Warum
untrack?initialist eine reaktive Prop. Ohneuntrackwarnt der Compiler, dasscountnur den ersten Wert übernimmt und danach nicht mehr mitinitialsynchron bleibt.untrackmacht diese gewollte Entkopplung explizit und stellt die Warnung still. Wenncountdoch automatisch synchron bleiben soll, ist Variante 2 ($bindable) oder ein$effectdie richtige Wahl. -
Two-Way-Binding mit
$bindable— siehe Artikel$bindable.
Migration von Svelte 4
| Svelte 4 | Svelte 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, $$restProps | let { ...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.
<!-- Wenn Props nach class kommen, überschreiben sie eigene Klassen -->
<button class="btn" {...rest} />
<!-- Eigene Klasse gewinnt -->
<button {...rest} class="btn" />