05 - Bindable ($bindable)

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>

Key Points

  • Creates two-way binding between parent and child
  • Use sparingly — prefer one-way data flow
  • Replaces Svelte 4's implicit binding on export let — now you 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

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>