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 to0if not bound$bindable()— defaults toundefinedif 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()forbind: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>