S SvelteKurs
M3 · Props & Kommunikation

$bindable — Zwei-Wege-Bindung

Normalerweise fließen Props nur nach unten. Mit $bindable öffnest du gezielt einen Rückkanal, sodass ein Kind einen Wert auch nach oben zurückschreiben kann.

Warum $bindable?

Props sind in Svelte 5 standardmäßig einweg: Die Eltern-Komponente gibt einen Wert nach unten, das Kind liest ihn. Manchmal — vor allem bei Formular-Bausteinen — soll das Kind den Wert aber auch ändern und die Änderung soll oben ankommen. Genau dafür markierst du eine Prop mit $bindable().

TextFeld.svelte svelte
<script lang="ts">
  interface Props {
    value: string;
  }

  // $bindable() macht die Prop zwei-Wege-fähig
  let { value = $bindable() }: Props = $props();
</script>

<input bind:value />

Durch value = $bindable() erlaubst du der Eltern-Komponente, mit bind:value zu koppeln. Du kannst auch einen Default mitgeben, falls nicht gebunden wird:

js
// Mit Default-Wert, falls die Eltern nicht binden:
let { value = $bindable('') } = $props();

Eltern binden mit bind:

Auf der Elternseite nutzt du das bind:-Präfix. Ab jetzt teilen sich Eltern-State und Kind-Prop denselben Wert: Tippt der Nutzer im Kind, aktualisiert sich name in den Eltern — und umgekehrt.

Formular.svelte svelte
<script lang="ts">
  import TextFeld from './TextFeld.svelte';

  let name = $state('Anna');
</script>

<!-- bind: koppelt Eltern-State und Kind-Prop in BEIDE Richtungen -->
<TextFeld bind:value={name} />

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

🛤️ Rails-Vergleich: form_with / Objekt-Binding

Das erinnert an form_with model: in Rails, wo Formularfelder an ein Objekt gekoppelt sind. Der Unterschied: Rails bindet über einen Request-Zyklus (Submit → Controller → zurück). bind: in Svelte ist dagegen sofort und im Browser — jede Tasteneingabe ist unmittelbar auf beiden Seiten sichtbar.

users/_form.html.erb erb
<%# Rails form_for bindet ein Objekt an Formularfelder %>
<%= form_with model: @user do |f| %>
  <%= f.text_field :name %>
  <%# Beim Submit landet der Wert in params[:user][:name],
      der Controller schreibt ihn zurück ins @user-Objekt %>
<% end %>

Live: Zwei-Wege-Bindung in Aktion

Svelte bringt bind:value für native Formularelemente von Haus aus mit — das ist dieselbe Zwei-Wege-Mechanik, die $bindable für deine eigenen Komponenten freischaltet. Tippe unten und beobachte, wie die Anzeige sofort mitläuft:

▶ bind:value · Interaktiv
Hallo, Anna!

Der State steht direkt hier in der Seite — bind: hält Input und Anzeige synchron.

Wann NICHT binden

⚠️ Zwei-Wege-Bindung sparsam einsetzen

bind: ist bequem, macht den Datenfluss aber schwerer nachvollziehbar, weil Änderungen aus beiden Richtungen kommen. Als Faustregel: Halte Daten einweg, wo es geht. Nutze $bindable nur für echte Formular-Bausteine (eigenes Input, Slider, Toggle), bei denen der zurückgegebene Wert der Zweck der Komponente ist.

svelte
<!-- Einweg (Standard, meistens richtig): Kind kann nur LESEN -->
<Anzeige value={name} />

<!-- Zwei-Wege (nur wo nötig): Kind darf auch SCHREIBEN -->
<TextFeld bind:value={name} />

🚫 Anti-Pattern

Verwende bind: nicht als Abkürzung, um Zustand zwischen Komponenten „irgendwie" zu synchronisieren. Für Kind→Eltern-Kommunikation, die eher ein Ereignis ist (Speichern, Löschen, Auswählen), nimm besser Callback-Props — dazu mehr in der nächsten Lektion.

Zusammenfassung

💡 Das Wichtigste

  • let { value = $bindable() } = $props() macht eine Prop zwei-Wege-fähig.
  • Eltern koppeln mit <Kind bind:value={...} />.
  • Native Elemente können bind:value direkt — ohne $bindable.
  • Faustregel: Datenfluss einweg halten, $bindable nur für Formular-Bausteine.

🎯 Probier es selbst

Ergänze die Demo um einen zweiten Input für einen gruss-Text (z. B. „Hallo", „Servus"). Binde ihn mit bind:value und baue ihn in die Anzeige ein — etwa {gruss}, {name}!. Achte darauf, dass beide Inputs sofort durchschlagen.