Component Communication

Chapter 7
Component Communication
Callback props for child-to-parent, and $bindable for two-way component binding

Props (Chapter 6) send data down. To send something back up — a click, a deletion, a chosen value — Svelte 5's primary approach is a callback prop: the parent passes a function down as a prop, and the child calls it. This is exactly React's model (Project 1's todo delete), and a notable shift from Svelte 4's event system.

Callback Props — Child Calls a Function

<!-- TodoItem.svelte --> <script> let { id, text, onDelete } = $props(); </script> <li> {text} <button onclick={() => onDelete(id)}>Delete</button> </li>
<!-- parent --> <TodoItem id={todo.id} text={todo.text} onDelete={removeTodo} />

onDelete is just another prop — a function. The child calls onDelete(id) when its button is clicked, and the parent's removeTodo receives the id. There's no special "emit" mechanism (unlike Angular's @Output or Vue's defineEmits) — it's a plain function passed as a prop, identical to how React does it. Naming it onSomething is the convention, matching the event-handler naming.

Svelte 4's createEventDispatcher is gone in v5
Pre-Svelte-5 code used createEventDispatcher() plus dispatch('delete', id) in the child and on:delete={handler} in the parent — a custom-event system much like Angular's @Output. Svelte 5 removed this in favor of callback props, simplifying the model to match React's. If you see createEventDispatcher or on:customEvent on a component, that's Svelte 4 code.

Multiple Callbacks

<!-- Dialog.svelte --> let { onSave, onCancel } = $props();
<button onclick={() => onSave(formData)}>Save</button> <button onclick={onCancel}>Cancel</button>

A component takes as many callback props as it needs, each a separate function. This scales cleanly — a dialog with onSave and onCancel, a form with onSubmit and onReset — all just functions passed down, with payloads passed as arguments when calling them.

$bindable — Two-Way Component Binding

Sometimes you want genuine two-way binding on a custom component — like a reusable input where the parent uses bind:value directly on it. The $bindable() rune marks a prop as bindable from the parent:

<!-- CustomInput.svelte --> <script> let { value = $bindable('') } = $props(); </script> <input bind:value={value} />
<!-- parent: bind: works directly on the component --> <CustomInput bind:value={name} />

$bindable('') declares value as a prop the parent can two-way bind (with a default of ''). The child binds it to its real input; the parent uses bind:value={name} on the component itself, and changes flow both ways automatically. This is Svelte's equivalent of Vue's defineModel / v-model on a component, and Angular's value/valueChange two-way convention.

Callback props vs $bindable — which to use
Reach for a callback prop when the child reports an event the parent handles however it likes (delete, save, select) — the common case. Reach for $bindable only when you specifically want bind: ergonomics on a component, like a reusable form control. Most communication is callback props; $bindable is the more specialized tool, and Svelte deliberately requires opting in (a prop isn't bindable unless declared so).
A child still doesn't reassign a normal prop
Outside of an explicit $bindable, the one-way rule from Chapter 6 holds: the child reports upward via a callback and lets the parent own the source of truth. $bindable is the deliberate exception, used sparingly — not a license to mutate ordinary props.
Svelte 5ReactVueAngular
onDelete prop (a function)callback prop onDeletedefineEmits + emit@Output + emit
onDelete(id)onDelete(id)emit('delete', id)this.delete.emit(id)
$bindable + bind:valuevalue + onChange propsdefineModel / v-model[(value)]

Coding Challenges

Challenge 1

Build a TodoItem component with id/text props and an onDelete callback prop. The parent holds a $state array of todos, renders one TodoItem each (keyed), and removes the matching todo when onDelete fires.

📄 View solution
Challenge 2

Build a ConfirmBar component with onConfirm and onCancel callback props (two buttons), and use it in a parent that logs which action was taken.

📄 View solution
Challenge 3

Build a CustomInput component exposing a $bindable value prop, then use bind:value on it in a parent and display the bound value live.

📄 View solution

Chapter 7 Quick Reference

  • Callback prop — pass a function down (onDelete); the child calls it (= React's model)
  • No special emit mechanism — it's a plain function prop, unlike Angular's @Output / Vue's defineEmits
  • Name callbacks onSomething; pass payloads as arguments when calling
  • $bindable(default) — marks a prop as two-way bindable, so the parent can use bind: on the component
  • Use callbacks for events (the common case); $bindable only for genuine two-way control bindings
  • Svelte 5 dropped Svelte 4's createEventDispatcher in favor of callback props
  • Next chapter: snippets and slots — Svelte's content-projection model