Standardmäßig fließen Props in Svelte nur in eine Richtung: vom Eltern-Komponent zum Kind. Wenn das Kind den Wert verändern und der Eltern-Komponent das automatisch mitbekommen soll, muss eine Prop als zwei-weg-bindbar markiert werden — und zwar explizit. Genau das macht $bindable. Dieser Artikel zeigt, wann du das brauchst, wie es im Vergleich zu einer Callback-Prop aussieht und warum die Explizitheit Absicht ist.

Grundprinzip

svelte NumberInput.svelte
<script>
    let { value = $bindable(0) } = $props();
</script>

<input type="number" bind:value />

Zwei Dinge passieren in dieser einen Zeile:

  • $bindable(0) markiert value als zwei-weg-bindbar. Damit darf der Eltern-Komponent später ein bind:value darauf nutzen.
  • Der Wert 0 dient als Default — wird gebraucht, wenn der Eltern-Komponent gar keinen Wert setzt.

Innerhalb der Komponente kannst du value jetzt wie eine ganz normale $state-Variable behandeln: lesen, neu zuweisen, in bind:value={value} für ein Form-Element einsetzen.

svelte Verwendung im Eltern-Komponent
<script>
    import NumberInput from '$lib/components/NumberInput.svelte';
    let count = $state(10);
</script>

<NumberInput bind:value={count} />
<p>Aktuell: {count}</p>

Wenn der Nutzer im <input> der Kind-Komponente einen neuen Wert eingibt, propagiert das automatisch nach count im Eltern-Komponente.

Mit oder ohne bind:

Eine $bindable-Prop kann zwei-weg-gebunden werden, muss es aber nicht. Wird sie ohne bind: übergeben, verhält sie sich wie eine normale Prop:

svelte Beide Varianten möglich
<!-- Two-Way-Binding -->
<NumberInput bind:value={count} />

<!-- Read-only — Updates der Kind-Komponente werden ignoriert -->
<NumberInput value={42} />

Damit lässt sich $bindable flexibel anbieten, ohne den Aufrufer zu zwingen.

Pflicht-Bindable: ohne Default

Wenn die Komponente immer ein bind:value erwartet, lässt sich der Default weglassen:

svelte Strict Bindable
<script>
    let { value = $bindable() } = $props();
</script>

Wer die Komponente ohne bind:value einbindet, bekommt einen Compiler-Hinweis. Das ist sinnvoll, wenn die Komponente ohne externen State sinnlos wäre — z. B. ein Form-Element, das keinen eigenen lokalen Default kennt.

Mehrere bindbare Props

Eine Komponente kann mehrere bindbare Props haben — typisch bei Range-Slidern, Datumsbereichen oder X/Y-Koordinaten:

svelte DateRange.svelte
<script>
    let {
        start = $bindable(new Date()),
        end = $bindable(new Date()),
    } = $props();
</script>

<input type="date" bind:value={start} />
<input type="date" bind:value={end} />
svelte Verwendung
<DateRange bind:start={from} bind:end={to} />

Wann $bindable, wann Callback-Prop?

Aspekt$bindableCallback-Prop
Aufruf am Eltern-Komponentbind:value={count}value={count} onChange={(v) => count = v}
Eltern muss $state haltenJaJa
Kind kann Wert direkt mutierenJaNein, nur Callback ausführen
Implizit im MarkupJaExplizit
Einfacher zu typisierenJaEtwas verbose
Ausdruck im Eltern komplexerSchwierigSehr flexibel (Validierung, Side Effects)

Faustregel: Für Form-Inputs, Picker und ähnliche „dumme” Werte-Inhaber ist $bindable ergonomischer. Sobald komplexe Update-Logik im Eltern nötig ist (Validierung, Throttling, parallele Updates), liefert eine Callback-Prop mehr Kontrolle.

TypeScript-Typen für bindable Props

Auch $bindable lässt sich sauber typisieren:

svelte TypeScript-Variante
<script lang="ts">
    type Props = {
        value?: number;
        step?: number;
    };

    let { value = $bindable(0), step = 1 }: Props = $props();
</script>

<input type="number" bind:value step={step} />

Wichtig: Im Type-Annotation ist die Prop optional (value?: number), weil sie einen Default hat. Ohne Default und mit Pflicht-Bindable wäre value: number und value = $bindable().

Migration von export let mit Bind

In Svelte 4 war jede export let-Variable implizit bindbar. Eltern-Komponenten konnten bind:value={...} nutzen, ohne dass die Kind-Komponente das markieren musste. In Svelte 5 ist das explizit: Nur Props, die mit $bindable(...) markiert sind, können zwei-weg-gebunden werden.

svelte Svelte 4
<script>
    export let value = 0;
</script>

<input type="number" bind:value />
svelte Svelte 5
<script>
    let { value = $bindable(0) } = $props();
</script>

<input type="number" bind:value />

Die explizite Markierung erleichtert das API-Verständnis: Auf einen Blick sichtbar, welche Props bidirektional sind.

Häufige Stolperfallen

bind:value ohne $bindable in der Kind-Komponente. Compiler-Fehler. Die Prop muss explizit markiert sein.

$bindable mit nicht-mutierbarer Quelle im Eltern.

svelte − Konstante als bind-Quelle
<NumberInput bind:value={42} />

42 ist kein State — es lässt sich nicht zurückschreiben. Im Eltern muss eine $state-Variable stehen.

$bindable für berechnete Werte. Wenn der Wert im Eltern eigentlich eine Ableitung ist, passt $bindable nicht. Stattdessen die Quelle reaktiv halten und ableiten.

$bindable mit komplexen Objekten. Funktioniert, wenn das Objekt selbst reaktiv ist ($state). Bei nicht-reaktiven Objekten verlieren tiefe Mutationen ihre Wirkung.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Reactivity (Runes)

Zur Übersicht