Events and bindings

Svelte · Essentials · 10 min

Interaction and forms are how users change state; bindings remove most form boilerplate.

In Svelte 5 event handlers are just attributes whose names start with on: onclick={handler}, oninput={(e) => ...}, onsubmit={save}. If the handler variable has the same name, use the shorthand {onclick}.

There are no event modifiers any more (the old on:click|preventDefault syntax is legacy). Call e.preventDefault() yourself, or wrap handlers in a small helper. For capture, use onclickcapture. Svelte uses event delegation for common events like click and input: one listener at the root dispatches to your handlers, which is efficient but means e.stopPropagation() in a manually added listener can interfere.

Bindings create two-way links between DOM properties and state with bind::

  • bind:value={name} on inputs, textareas and selects (type="number" and range give numbers).
  • bind:checked={agree} on checkboxes.
  • bind:group={choice} on a set of radios (single value) or checkboxes (array of values).
  • bind:this={el} gives you the DOM element (set after mount).
  • Read-only dimension bindings such as bind:clientWidth={w}.

The shorthand bind:value binds to a variable called value. Bindings need a $state variable (or a bindable prop) on the other end. For custom conversion, Svelte 5.9+ supports function bindings: bind:value={() => get(), (v) => set(v)}.

Example

<script>
  let name = $state('');
  let age = $state(30);
  let agree = $state(false);
  let flavours = $state(['vanilla']);
  let log = $state([]);

  function onsubmit(e) {
    e.preventDefault();                       // no modifiers in Svelte 5
    log.push(`${name} (${age}) ${flavours.join('+')}`);
  }
</script>

<form {onsubmit}>
  <input bind:value={name} placeholder="name" />
  <input type="number" bind:value={age} />   <!-- age stays a number -->
  {#each ['vanilla', 'chocolate'] as f}
    <label><input type="checkbox" value={f} bind:group={flavours} /> {f}</label>
  {/each}
  <label><input type="checkbox" bind:checked={agree} /> I agree</label>
  <button disabled={!agree}>Order</button>
</form>
<p>{log.join(', ')}</p>

Remember

  • Events are attributes: onclick={fn}; call e.preventDefault() yourself
  • bind:value, bind:checked, bind:group keep inputs and state in sync
  • bind:this gives the element once mounted

Watch out for

  • Writing onclick={doThing()}, which calls the function during render; pass doThing or () => doThing().

Check yourself

  1. How do you prevent a form's default submission in Svelte 5 runes mode?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: svelte.dev/docs/svelte/bind