Updated Jul 16, 2026

05 - Bindable ($bindable)

📋 Jump to Takeaways

🎁 A parent passes count to a child, the child changes it, and the parent's copy updates too, both directions from a single line. When would you actually want data to flow backward like that?

What is $bindable?

$bindable() creates two-way bindable props. Allows parent components to bind to child component props.

In Svelte 4, every export let prop was implicitly bindable. In Svelte 5, you must explicitly mark which props support two-way binding using $bindable() inside $props().

Svelte 4 (Legacy)

<!-- Child.svelte -->
<script>
export let value = 0;  // every export let was implicitly bindable
</script>

Svelte 5

<!-- Child.svelte -->
<script>
let { value = $bindable(0) } = $props();  // explicitly opt-in to binding
</script>

The parent side stays the same in both versions: <Child bind:value={count} />

Basic Usage

Child Component:

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

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

Parent Component:

<script>
import Child from './Child.svelte';
let count = $state(0);
</script>

<Child bind:value={count} />
<p>Parent sees: {count}</p>

With Objects

Child:

<script>
let { user = $bindable({ name: '', age: 0 }) } = $props();
</script>

<input bind:value={user.name} />
<input type="number" bind:value={user.age} />

Parent:

<script>
let user = $state({ name: 'Alice', age: 25 });
</script>

<Child bind:user />

Optional Bindable

Calling $bindable() with no default means value is undefined if the parent doesn't bind to it. Compare:

  • $bindable(0) defaults to 0 if not bound
  • $bindable() defaults to undefined if not bound

Useful when a component optionally supports two-way binding.

<script>
let { value = $bindable() } = $props();
// value is undefined if parent doesn't use bind:value
</script>

Interactive Example

Child Component (05-bindable-child.svelte):

<script>
// Child component with bindable value
let { value = $bindable(0) } = $props();
</script>

<div class="input-group">
  <button onclick={() => value--}>-</button>
  <input type="number" bind:value />
  <button onclick={() => value++}>+</button>
</div>

<style>
.input-group {
  display: flex;
  gap: 5px;
  align-items: center;
}
input {
  width: 80px;
  text-align: center;
}
</style>

Parent Component (05-bindable.svelte):

<script>
import Child from './05-bindable-child.svelte';

let count = $state(10);
let doubled = $derived(count * 2);
</script>

<div>
  <h2>05 - Bindable Example</h2>
  
  <p>Parent count: {count}</p>
  <p>Doubled: {doubled}</p>
  
  <h3>Child component (two-way bound):</h3>
  <Child bind:value={count} />
  
  <p>Changes in child automatically update parent!</p>
</div>

<style>
div { margin: 20px; }
h3 { margin-top: 20px; }
</style>

Key Takeaways

  • Creates two-way binding between parent and child
  • Use sparingly. Prefer one-way data flow
  • Replaces Svelte 4's implicit binding on export let. You now explicitly opt in with $bindable()
  • Parent uses bind:prop={variable} (same as Svelte 4)
  • Child must wrap the prop default with $bindable() for bind: to work

🎁 You can pass values into a component. But what about passing a whole chunk of markup, like a custom row template or a card layout, for the component to render wherever it wants? How does Svelte hand blocks of UI around?

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

Spot something off? Report an issue
© 2026 ByteLearn.dev. Free courses for developers. · Privacy