Under The Hood
frontend svelte Svelte 5 (runes)

$props() Destructuring and $bindable() Two-Way Bindings

Last updated
Prerequisites:
Svelte 5 runes basics
Understanding of props and bindings in Svelte 4
  • svelte
  • runes
  • props
  • bindable
  • two-way-binding
  • component-api

Read at your depth

The practical view

In Svelte 5, props are declared with $props(): let { name, age = 18 } = $props();. Destructuring gives you local variables that update when the parent changes them. For two-way binding, mark a prop $bindable(): let { count, onChange } = $props(); and in the parent <Child bind:count>. Alternatively: let { count = $bindable(0) } = $props(). The child can assign to count (count += 1) and the parent's variable updates. This replaces Svelte 4's export let + bind: syntax.

The same idea in other frameworks

react equivalent
Destructured props are read-only; two-way requires lifting state + callbacks — Svelte's $bindable is sugar for that.

Legacy vs modern

Svelte 4 export let + bind vs Svelte 5 $props/$bindable

Svelte 4's export let made all props technically writable (ambiguous two-way); $props()/$bindable() make the contract explicit.

before → after
Svelte 4 export let
export let count = 0; // ❌ OLD: props via export let
// any prop could be assigned internally — no explicit two-way marker
Svelte 5 $props + $bindable
let { count = $bindable(0), label } = $props(); // ✅ NEW: explicit two-way marker

Interview gotchas

Context

This tests the two-way binding contract — interviewers want to know the write-propagation semantics and the silent-divergence trap when the parent doesn't bind.

The mechanical answer

$bindable() declares that the child may write to the prop, establishing a two-way channel: the child's assignment compiles into a write that propagates back to the parent's state through the bind: mechanism. If the parent renders <Child count={n}> WITHOUT bind:, the child's write has nowhere to propagate — the write updates the child's local prop value, but the parent's n does not change, so the UI diverges silently until the parent re-renders. This is why $bindable() props should be used with bind: in the parent, or the child should treat the prop as a controlled value and emit changes via a callback instead. The compiler helps by marking bindable props distinctly, but the parent's usage determines whether writes actually flow.

Trap

The naive answer is '$bindable just lets the child change its prop'. Without bind:, the parent never sees the change — the classic controlled-component pitfall. Another trap: assuming bindable props must always be used two-way; a common pattern is to pair a bindable with a change callback for controlled usage. Candidates who explain the bind: propagation requirement show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.