Svelte and SvelteKit course ยท Module 4: Props and Events
Events - Communicating Upward
In this lesson4
Events allow child components to send information to parents - like scout reports to the jarl! In Svelte 5 you do it with plain functions passed as props.
Callback Props (Svelte 5)
The parent passes a function as a prop, and the child calls it in an event attribute such as onclick:
<!-- WarriorCard.svelte -->
<script>
let { warrior, onSelect, onDelete } = $props();
</script>
<div class="card">
<h3>{warrior.name}</h3>
<button onclick={() => onSelect(warrior)}>
Select
</button>
<button onclick={() => onDelete(warrior.id)}>
Delete
</button>
</div><!-- Parent.svelte -->
<script>
import WarriorCard from './WarriorCard.svelte';
let warriors = $state([
{ id: 1, name: 'Ragnar' },
{ id: 2, name: 'Bjorn' }
]);
let selected = $state(null);
function handleSelect(warrior) {
selected = warrior;
console.log('Selected:', warrior.name);
}
function handleDelete(id) {
warriors = warriors.filter(w => w.id !== id);
}
</script>
{#each warriors as warrior (warrior.id)}
<WarriorCard
{warrior}
onSelect={handleSelect}
onDelete={handleDelete}
/>
{/each}
{#if selected}
<p>Selected: {selected.name}</p>
{/if}Start the names of such props with on (onSelect, onDelete) - it shows at once that they are events. The data reaches the parent directly as function arguments.
Element Events and Modifiers
In Svelte 5, DOM events are plain attributes: onclick, onkeydown, onsubmit. Modifiers like |preventDefault are gone - you call the matching method in the handler:
<script>
let report = $state('');
function sendReport(e) {
e.preventDefault(); // formerly on:submit|preventDefault
report = 'Report sent to the jarl!';
}
</script>
<form onsubmit={sendReport}>
<input placeholder="Report text" />
<button>Send</button>
</form>
<p>{report}</p>Forwarding Events
A component that wraps an element (a button, for example) takes a callback in its props and hands it to the element:
<!-- Button.svelte -->
<script>
let { onclick, children } = $props();
</script>
<button {onclick}>
{@render children()}
</button>You can also pass all remaining props to the element at once - together with onclick, class or disabled:
<!-- Button.svelte -->
<script>
let { children, ...rest } = $props();
</script>
<button {...rest}>
{@render children()}
</button><!-- Parent.svelte -->
<script>
import Button from './Button.svelte';
function handleClick() {
alert('To battle!');
}
</script>
<Button onclick={handleClick}>
Click me
</Button>A callback passes through an intermediate component like any other prop: App passes onAttack to Battle, and Battle passes it on to Fighter (see the editor below).
Svelte 4 (legacy): createEventDispatcher
In older code you will see createEventDispatcher and on: directives. In Svelte 5 createEventDispatcher is deprecated, and instead of <button on:click> (forwarding without a handler) you pass a callback as shown above. Recognize this style, but use callback props in new code.
<!-- WarriorCard.svelte - Svelte 4 (legacy) -->
<script>
import { createEventDispatcher } from 'svelte';
export let warrior;
const dispatch = createEventDispatcher();
</script>
<button on:click={() => dispatch('select', warrior)}>Select</button><!-- Parent.svelte - Svelte 4 (legacy) -->
<WarriorCard {warrior} on:select={(e) => selected = e.detail} />The data arrived in e.detail. With callback props you get it directly as a function argument.
Code for this lesson: App.svelte
<script>
// Callback props pass through components like any other prop
import Battle from './Battle.svelte';
let battleLog = $state([]);
function handleAttack(entry) {
battleLog = [...battleLog, entry].slice(-5);
}
</script>
<main class="arena">
<h1>Battle Arena</h1>
<p>App passes onAttack to Battle, Battle passes it on to each Fighter</p>
<Battle onAttack={handleAttack} />
<div class="log">
<h2>Battle log:</h2>
{#each battleLog as entry}
<p>{entry.attacker} dealt {entry.damage} damage!</p>
{/each}
{#if battleLog.length === 0}
<p class="empty">No attacks...</p>
{/if}
</div>
</main>
<style>
.arena {
font-family: 'Georgia', serif;
max-width: 600px;
margin: 0 auto;
padding: 2rem;
background: #1a0f0a;
color: #e8d5b7;
min-height: 100vh;
}
h1 {
color: #d4af37;
text-align: center;
}
p {
text-align: center;
color: #8b7355;
}
.log {
margin-top: 2rem;
background: rgba(0,0,0,0.3);
border-radius: 8px;
padding: 1rem;
}
.log h2 {
color: #d4af37;
font-size: 1rem;
margin-top: 0;
}
.log p {
text-align: left;
margin: 0.5rem 0;
padding: 0.5rem;
background: rgba(139, 0, 0, 0.2);
border-radius: 4px;
}
.empty {
color: #666;
font-style: italic;
}
</style>Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is the preferred way to send events in Svelte 5?
Hands-on tasks in the game
- Code editor
Complete the code so that clicking the button calls the onSelect function