Svelte 5 (Oktober 2024) ist die größte Version seit Jahren. Das Reactivity-Modell ist neu (Runes), Slots wurden durch Snippets ersetzt, Events haben eine neue Syntax und Komponenten sind keine Klassen mehr, sondern Funktionen. Bestehender Svelte-4-Code läuft weiter, aber neuer Code sollte den modernen Stil nutzen. Dieser Artikel zeigt die wichtigsten Änderungen mit Vorher/Nachher-Beispielen und stellt das automatische Migrations-Tool vor.

Überblick: Was hat sich geändert?

BereichSvelte 4Svelte 5
Reaktive Variablelet count = 0;let count = $state(0);
Abgeleiteter Wert$: doubled = count * 2;let doubled = $derived(count * 2);
Side Effect$: console.log(count);$effect(() => { console.log(count); });
Propsexport let name;let { name } = $props();
Default-Wertexport let name = 'Anna';let { name = 'Anna' } = $props();
Eventon:click={handler}onclick={handler}
Component-EventcreateEventDispatcherCallback-Prop
Slot<slot />, <slot name>Snippet ({#snippet}, {@render})
Mountnew Component(...)mount(Component, ...)
Dynamic Component<svelte:component this={…}><{Component} /> direkt

Bestehender Code läuft im Legacy-Modus weiter — Svelte 5 erkennt am Code, ob ein Modul noch in Svelte-4-Konventionen geschrieben ist.

Reaktivität: Runes statt impliziter Reaktivität

In Svelte 4 war jede let-Variable im Top-Level einer Komponente automatisch reaktiv. In Svelte 5 markiert man das explizit mit $state(...).

svelte Svelte 4
<script>
    let count = 0;
    $: doubled = count * 2;
    $: if (count > 5) console.log('groß!');
</script>

<button on:click={() => count++}>
    {count} (doppelt: {doubled})
</button>
svelte Svelte 5
<script>
    let count = $state(0);
    let doubled = $derived(count * 2);

    $effect(() => {
        if (count > 5) console.log('groß!');
    });
</script>

<button onclick={() => count++}>
    {count} (doppelt: {doubled})
</button>

Vorteile der Runes:

  • Explizit. Auf einen Blick sichtbar, was reaktiv ist.
  • Außerhalb von Komponenten nutzbar. In .svelte.js oder .svelte.ts-Dateien lassen sich eigene reaktive Helfer schreiben.
  • Klar abgegrenzt zwischen Ableitung ($derived) und Side Effect ($effect). Beides war in Svelte 4 derselbe $:-Block.

Props mit $props()

Statt einzelner export let-Statements gibt es eine destrukturierte Variante:

svelte Svelte 4
<script>
    export let name;
    export let age = 30;
</script>
svelte Svelte 5
<script>
    let { name, age = 30 } = $props();
</script>

Mit Rest-Operator funktionieren auch unbekannte Props:

svelte Rest-Props weiterreichen
<script>
    let { class: cssClass, ...rest } = $props();
</script>

<button class={cssClass} {...rest}>
    ...
</button>

Events: on:click -> onclick

In Svelte 5 sind DOM-Event-Handler ganz normale Attribute — keine eigene Syntax mehr:

svelte Svelte 4
<button on:click={handle} on:mouseenter={hover}>
    Klick
</button>
svelte Svelte 5
<button onclick={handle} onmouseenter={hover}>
    Klick
</button>

Event-Modifier wie on:click|preventDefault|once gibt es nicht mehr. Stattdessen schreibt man die Logik im Handler selbst:

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

<!-- Svelte 5 -->
<form onsubmit={(e) => { e.preventDefault(); save(); }}>...</form>

Component-Events als Callback-Props

createEventDispatcher ist Geschichte. Custom Component Events werden als Callback-Props modelliert:

svelte Svelte 4 – Child
<script>
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();
</script>

<button on:click={() => dispatch('save', { value: 42 })}>
    Speichern
</button>
svelte Svelte 5 – Child
<script>
    let { onSave } = $props();
</script>

<button onclick={() => onSave({ value: 42 })}>
    Speichern
</button>
svelte Svelte 5 – Verwendung
<SaveButton onSave={(detail) => console.log(detail)} />

Dieser Wechsel macht Component-APIs typisierbar und einheitlich mit DOM-Events.

Slots -> Snippets

Slots sind durch das mächtigere Snippet-System abgelöst. Inhalte zwischen Komponenten-Tags werden zur Snippet-Prop children:

svelte Svelte 4 – Card
<div class="card">
    <slot />
</div>
svelte Svelte 5 – Card
<script>
    let { children } = $props();
</script>

<div class="card">
    {@render children()}
</div>

Named Slots werden zu eigenen Snippet-Props:

svelte Svelte 4 – Named Slots
<article>
    <slot name="header" />
    <slot />
    <slot name="footer" />
</article>
svelte Svelte 5 – Snippet-Props
<script>
    let { header, footer, children } = $props();
</script>

<article>
    {@render header?.()}
    {@render children()}
    {@render footer?.()}
</article>
svelte Verwendung
<Article>
    {#snippet header()}
        <h2>Titel</h2>
    {/snippet}

    <p>Hauptinhalt — landet automatisch in children.</p>

    {#snippet footer()}
        <small>Quelle</small>
    {/snippet}
</Article>

Snippets können — anders als Slots — Argumente annehmen. Damit lassen sich Slots, die früher mit let:variable exportiert wurden, sauber typisieren.

Komponenten als Funktionen

In Svelte 4 waren Komponenten Klassen, die mit new instanziiert wurden:

TypeScript Svelte 4
import App from './App.svelte';

const app = new App({
    target: document.getElementById('app'),
    props: { name: 'Welt' },
});

In Svelte 5 sind Komponenten Funktionen — gemountet wird mit mount(...):

TypeScript Svelte 5
import { mount } from 'svelte';
import App from './App.svelte';

const app = mount(App, {
    target: document.getElementById('app'),
    props: { name: 'Welt' },
});

Auch bind:this auf Komponenten gibt heute keinen Klassen-Instanz-Wert mehr zurück, sondern ein Objekt mit den exportierten Werten/Funktionen.

Dynamische Komponenten

<svelte:component this={...} /> wird seltener gebraucht — eine Komponenten-Variable kann direkt als Tag-Name benutzt werden:

svelte Svelte 5
<script>
    import IconA from './IconA.svelte';
    import IconB from './IconB.svelte';

    let Icon = $state(IconA);
</script>

<Icon />
<button onclick={() => Icon = IconB}>Tauschen</button>

Automatische Migration

Das offizielle CLI-Tool sv enthält einen Migrationsbefehl, der einen Großteil des Codes automatisch umstellt:

bash Migrations-Tool ausführen
npx sv migrate svelte-5

Was das Tool typischerweise erledigt:

  • let$state (mit Heuristik, was tatsächlich reaktiv ist)
  • $:$derived oder $effect
  • export let$props()
  • on:eventonevent

Manuell nachzuziehen sind meist:

  • createEventDispatcher ⇒ Callback-Props
  • Komplexe beforeUpdate/afterUpdate-Logik ⇒ $effect.pre / $effect
  • Slots mit Pattern, die sich nicht 1:1 abbilden lassen

Tipp: Migrations-Diffs klein halten — Datei für Datei migrieren, jeden Schritt committen, Tests dazwischen ausführen.

Was bleibt gleich?

  • .svelte-Dateiformat mit <script>, Markup und <style>.
  • Stores (writable, readable, derived) funktionieren weiter.
  • Lifecycle-Funktionen onMount und onDestroy existieren weiter.
  • Transitions, Actions, Animations sind im Wesentlichen unverändert.
  • SvelteKit läuft mit Svelte 5; ältere Projekte können schrittweise migrieren.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Grundlagen

Zur Übersicht