Svelte 17: Callback Props — Component-to-Parent Communication
easy⏱ 5 mincoursesvelte
A callback prop is just... a function
The child declares it like any other prop — let { onAddToCart } = $props(); — and calls it whenever it wants to notify the parent, passing along whatever data is relevant: onAddToCart(item). The parent passes a real function on the component tag: <Product onAddToCart={(item) => cart.push(item)} />. There's no indirection, no string event names — it's regular JavaScript function-passing, all the way down.
<!-- Product.svelte -->
<script>
let { name, onAddToCart } = $props();
</script>
<button onclick={() => onAddToCart({ name })}>Add to cart</button>
<!-- App.svelte -->
<script>
import Product from './Product.svelte';
let cart = $state([]);
</script>
<Product name='Mug' onAddToCart={(item) => cart.push(item)} />
What this replaces: createEventDispatcher (Svelte 4)
Svelte 4 required importing createEventDispatcher, calling dispatch('addToCart', item) inside the child, and listening with the on:addToCart={...} directive on the parent's component tag. It worked, but it's more ceremony for the exact same outcome, and the event name was just a string — a typo in either the dispatch call or the on: directive failed silently. The read-only snippet below shows that old shape purely for comparison; you are NOT expected to write it.
<!-- Svelte 4 style — shown for comparison only, do NOT use in new code -->
<!-- Product.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let name;
</script>
<button on:click={() => dispatch('addToCart', { name })}>Add to cart</button>
<!-- App.svelte -->
<Product name='Mug' on:addToCart={(e) => cart.push(e.detail)} />
Callback props vs $bindable — which one?
Use a callback prop (onSomething) when the child is reporting an EVENT or a one-off piece of data — a click, a submission, an item added. Use $bindable when the child is the natural OWNER of a continuously-updated value that the parent also needs live access to, like a form control's current value. Most parent/child communication in real apps is callback props; $bindable is the special case.