Events haben sich in Svelte 5 spürbar geändert: DOM-Events sind ganz normale HTML-Attribute (onclick={...} statt on:click={...}), Component-Events werden als Callback-Props modelliert statt mit createEventDispatcher und die Modifier-Syntax (|preventDefault|once) ist entfallen. Die neue Form ist konsistenter, einfacher zu typisieren und verhält sich identisch zum DOM. Dieser Artikel zeigt alle Varianten und die Migration aus Svelte 4.

DOM-Events

Jeder Event-Handler ist ein normales Attribut:

svelte DOM-Events
<script>
    function handleClick(event) {
        console.log('clicked', event.target);
    }
</script>

<button
    onclick={handleClick}
    onmouseenter={() => console.log('hover')}
>
    Klick
</button>

Das Event-Objekt steht als erstes Argument zur Verfügung, mit korrektem Type bei lang="ts".

Inline-Handler

svelte Inline
<button onclick={() => count++}>
    +1
</button>

Inline-Handler sind in Svelte üblich, solange sie kurz bleiben.

Event-Modifier ersetzen

In Svelte 4 gab es Modifier wie |preventDefault, |once, |capture. In Svelte 5 schreibt man die Logik im Handler selbst:

svelte preventDefault
<!-- Svelte 4 -->
<form on:submit|preventDefault={save}>...</form>

<!-- Svelte 5 -->
<form onsubmit={(e) => { e.preventDefault(); save(); }}>...</form>
svelte once
<script>
    let handled = $state(false);

    function handle() {
        if (handled) return;
        handled = true;
        // ...
    }
</script>

<button onclick={handle}>Einmalig</button>

Für capture-Listener gibt es das passende Suffix direkt im Attribut: onclickcapture={...}.

TypeScript für DOM-Events

Mit lang="ts" lassen sich Event-Typen ableiten:

svelte Typisierte Events
<script lang="ts">
    function handleSubmit(event: SubmitEvent) {
        event.preventDefault();
        const form = event.currentTarget as HTMLFormElement;
        const formData = new FormData(form);
        console.log(Object.fromEntries(formData));
    }
</script>

<form onsubmit={handleSubmit}>
    <input name="email" type="email" />
    <button type="submit">Senden</button>
</form>

Wer den Typ inline ableiten will, nutzt einen kurzen Cast: (e: Event) => { ... } oder die spezifischeren Sub-Typen wie MouseEvent, KeyboardEvent, SubmitEvent.

Component-Events als Callback-Props

createEventDispatcher ist Geschichte. Eigene Events einer Kind-Komponente werden als Funktionen (Callback-Props) modelliert:

svelte SaveButton.svelte (Kind)
<script>
    let { onSave } = $props();
</script>

<button onclick={() => onSave?.({ timestamp: Date.now() })}>
    Speichern
</button>
svelte Verwendung im Eltern
<SaveButton onSave={(detail) => console.log(detail.timestamp)} />

Vorteile:

  • Keine Dispatcher-Boilerplate mehr.
  • Direkte Typisierbarkeit: onSave: (detail: SaveDetail) => void ist eine normale Funktions-Signatur.
  • Konsistent mit DOM-Events: Der Aufrufer schreibt onSave={...} — wie bei onclick={...}.
  • Optional aufrufbar: onSave?.() rendert die Komponente auch ohne Listener fehlerfrei.

Naming-Konvention für Component-Events

Die etablierte Konvention im React- und Svelte-5-Ökosystem: Callback-Props beginnen mit on und kommen in camelCase.

AnlassProp-Name
Speichern-AktiononSave
Element selektierenonSelect
Eintrag löschenonDelete
Wert hat sich geändertonChange
Modal schließenonClose
Custom: Gestik abgeschlossenonSwipeDone

Damit wirken Komponenten-Events von außen identisch zu DOM-Events.

Event-Forwarding

Wenn deine Wrapper-Komponente einen DOM-Event direkt weiterreichen soll (z. B. ein Custom-Button, der den onclick des umschlossenen <button> exposed), nutzt du Spread:

svelte StyledButton.svelte
<script>
    let { children, ...rest } = $props();
</script>

<button class="styled" {...rest}>
    {@render children()}
</button>
svelte Verwendung
<StyledButton onclick={save} onmouseenter={hover}>
    Speichern
</StyledButton>

onclick und onmouseenter landen über den Spread direkt am inneren <button> — kein eigenes Forwarding nötig.

In Svelte 4 brauchte man dafür <button on:click on:mouseenter> (Forwarding-Syntax) — in Svelte 5 reicht der normale Spread.

TypeScript: Callback-Prop typisieren

svelte ProductCard.svelte
<script lang="ts">
    type Product = { id: string; name: string };

    type Props = {
        product: Product;
        onAddToCart?: (product: Product) => void;
        onRemove?: (id: string) => void;
    };

    let { product, onAddToCart, onRemove }: Props = $props();
</script>

<article>
    <h3>{product.name}</h3>
    <button onclick={() => onAddToCart?.(product)}>In den Warenkorb</button>
    <button onclick={() => onRemove?.(product.id)}>Entfernen</button>
</article>

Migration aus Svelte 4

text Migrations-Tabelle
Svelte 4                                          Svelte 5
------------------------------------------------  -----------------------------------------------------
on:click={handle}                                 onclick={handle}
on:click|preventDefault={handle}                  onclick={(e) => { e.preventDefault(); handle(); }}
on:click|once={handle}                            manuell mit if (handled) return;-Flag
on:click|capture={handle}                         onclickcapture={handle}
on:click (Forwarding)                             {...rest} weiterreichen
createEventDispatcher + dispatch('save', x)       Callback-Prop onSave={...}
<Component on:save={handle} />                    <Component onSave={handle} />

Häufige Stolperfallen

on:click in Svelte 5 verwendet. Funktioniert im Legacy-Modus, in einer Runes-Komponente führt das zu einer Compiler-Warnung. Auf onclick={...} umstellen.

Callback-Prop mit dispatch-Mindset bauen.

svelte − Falsch gedacht
<script>
    let { onChange } = $props();
</script>

<input oninput={(e) => onChange({ detail: { value: e.target.value } })} />
svelte + Direkter Wert
<input oninput={(e) => onChange?.(e.currentTarget.value)} />

Das event.detail-Konstrukt aus Svelte 4 ist nicht mehr nötig — eine Callback-Prop kann direkt das Nutz-Datum übergeben.

Vergessen, dass Callback-Prop optional sein kann. onSave?.(...) mit Optional-Chaining verhindert Fehler, falls die Eltern-Komponente keinen Listener anhängt.

bind:this mit Component-Events verwechseln. Wer auf eine Funktion einer Kind-Komponente zugreifen will, nutzt bind:this und kann dann Methoden aufrufen — aber das ist kein Event. Siehe Component-Bindings.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Components

Zur Übersicht