Two-Way Binding with bind:value

+15 Mana ✨

Normally, data in Svelte flows one way: from parent to child via props. But form inputs are special — we often want changes in the input to update our state immediately.

Svelte's bind: directive creates two-way binding: the state updates the input, and the input updates the state.

Basic Text Input

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

<input bind:value={name} />
<p>Hello, {name || 'stranger'}!</p>

As you type, name updates in real-time. If you change name programmatically, the input updates too.

Without bind: (One-Way)

To understand the difference, here's one-way binding:

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

<!-- This only displays the value, typing doesn't update `name` -->
<input value={name} />

<!-- You'd need to manually handle the input event -->
<input value={name} oninput={(e) => name = e.target.value} />

bind:value does this for you automatically!

Textarea

Works exactly like text inputs:

svelte
<script>
  let bio = $state('');
</script>

<textarea bind:value={bio} rows="5"></textarea>
<p>Character count: {bio.length}</p>

Number Inputs

Svelte automatically converts to numbers when using type="number":

svelte
<script>
  let age = $state(25);
  let quantity = $state(1);
</script>

<input type="number" bind:value={age} />
<input type="range" bind:value={quantity} min="1" max="10" />

<p>age is a {typeof age}</p>  <!-- "number", not "string"! -->

Common Form Elements

svelte
<script>
  let email = $state('');
  let password = $state('');
  let search = $state('');
</script>

<input type="email" bind:value={email} placeholder="Email" />
<input type="password" bind:value={password} placeholder="Password" />
<input type="search" bind:value={search} placeholder="Search..." />
✓ Completed