onDestroy ist das Gegenstück zu onMount: Sie läuft, kurz bevor die Komponente aus dem DOM entfernt wird. Typische Aufgaben sind das Beenden von Timern, Listenern, WebSocket-Verbindungen oder Subscriptions — also alles, was sonst nach dem Verschwinden der Komponente weiterlaufen würde. Eine Besonderheit: Im Gegensatz zu onMount läuft onDestroy auch beim Server-Side-Rendering.

Was ist onDestroy?

Wenn eine Komponente aus dem DOM verschwindet — weil ein {#if} falsch wird, der Nutzer auf eine andere Seite navigiert oder die App beendet wird — bekommt sie eine letzte Chance, aufzuräumen.

svelte Minimales Beispiel
<script>
    import { onDestroy } from 'svelte';

    onDestroy(() => {
        console.log('Komponente wird gleich entfernt');
    });
</script>

<p>Hallo</p>

Wichtig zu wissen:

  • Die Funktion wird kurz vor dem tatsächlichen Entfernen aufgerufen.
  • onDestroy läuft auch beim Server-Side-Rendering (im Gegensatz zu onMount). Das ist ein Unterschied, den man bei serverseitigem Code im Kopf behalten muss.
  • Es darf — wie onMount — nur im Top-Level des <script> stehen.

onDestroy vs. Cleanup-Rückgabe in onMount

Eine berechtigte Frage: Warum gibt es zwei Wege, denselben Job zu erledigen?

svelte Variante A: Cleanup in onMount
<script>
    import { onMount } from 'svelte';

    onMount(() => {
        const id = setInterval(() => { /* … */ }, 1000);
        return () => clearInterval(id);
    });
</script>
svelte Variante B: separates onDestroy
<script>
    import { onMount, onDestroy } from 'svelte';

    let id;

    onMount(() => {
        id = setInterval(() => { /* … */ }, 1000);
    });

    onDestroy(() => {
        clearInterval(id);
    });
</script>

Beides funktioniert. Empfehlung:

  • Wenn Setup und Cleanup eng zusammengehören -> onMount mit Rückgabefunktion. Lokal, kompakt, lesbar.
  • Wenn Cleanup unabhängig vom Setup ist oder auch beim SSR laufen soll -> onDestroy.

In den meisten Fällen ist Variante A ergonomischer.

Praxis-Beispiel: Store-Subscription

Eine Komponente abonniert einen Svelte-Store auf eine Art, die das automatische $store-Pattern nicht abdeckt — z. B. mit eigener Filter-Logik. Sie muss die Subscription wieder beenden, sonst läuft der Listener weiter:

svelte Subscription beenden
<script>
    import { onDestroy } from 'svelte';
    import { messages } from '$lib/stores/messages';

    let count = $state(0);

    const unsubscribe = messages.subscribe((all) => {
        count = all.filter((m) => m.unread).length;
    });

    onDestroy(unsubscribe);
</script>

<p>Ungelesen: {count}</p>

messages.subscribe(...) gibt selbst eine Funktion zurück, die die Subscription beendet. Wir reichen sie direkt an onDestroy weiter — kompakt und klar.

Praxis-Beispiel: WebSocket schließen

svelte Live-Updates
<script>
    import { onMount, onDestroy } from 'svelte';

    let messages = $state([]);
    let socket;

    onMount(() => {
        socket = new WebSocket('wss://example.com/feed');
        socket.onmessage = (event) => {
            messages.push(JSON.parse(event.data));
        };
    });

    onDestroy(() => {
        socket?.close();
    });
</script>

<ul>
    {#each messages as msg (msg.id)}
        <li>{msg.text}</li>
    {/each}
</ul>

Hier zeigt sich der Vorteil von onDestroy als eigene Funktion: Wir öffnen den Socket im onMount, halten ihn in einer Modul-Variable, und schließen ihn klar beim Unmount.

SSR-Unterschied im Detail

Beim Server-Side-Rendering wird die Komponente einmal gerendert, das HTML zur Antwort geschickt, und die Komponente am Server „zerstört”. onDestroy läuft dabei. onMount nicht.

Das ist relevant, wenn dein onDestroy-Code Browser-spezifische APIs verwendet:

svelte − Crasht beim SSR
<script>
    import { onDestroy } from 'svelte';

    onDestroy(() => {
        window.removeEventListener('resize', handleResize); // crasht auf Server
    });
</script>
svelte + Sicher
<script>
    import { onMount, onDestroy } from 'svelte';

    function handleResize() { /* … */ }

    onMount(() => {
        window.addEventListener('resize', handleResize);

        // Cleanup-Variante: läuft nur im Browser
        return () => window.removeEventListener('resize', handleResize);
    });
</script>

Faustregel: Wenn Cleanup-Code Browser-APIs braucht, gehört er in die Rückgabefunktion von onMount. Dann läuft er nicht auf dem Server.

Mehrere onDestroy-Aufrufe

Du kannst onDestroy mehrfach in derselben Komponente aufrufen — jeder Callback wird beim Unmount ausgeführt:

svelte Mehrere Cleanups
<script>
    import { onDestroy } from 'svelte';
    import { logger } from '$lib/logger';

    const unsubscribeA = storeA.subscribe(/* … */);
    const unsubscribeB = storeB.subscribe(/* … */);

    onDestroy(unsubscribeA);
    onDestroy(unsubscribeB);
    onDestroy(() => logger.flush());
</script>

Die Reihenfolge der Aufrufe entspricht der Reihenfolge, in der onDestroy im Code steht.

Häufige Stolperfallen

onDestroy in Bedingung. Wie onMount: Aufruf nur im Top-Level. Bedingungen kommen in den Callback.

Auf nicht-existente Variablen zugreifen. Wenn socket nur im onMount zugewiesen wird, kann er beim ersten SSR-Durchlauf in onDestroy undefined sein. Mit Optional-Chaining (socket?.close()) absichern.

Unsicheres unsubscribe aufrufen. Manche Store-Bibliotheken liefern bei Subscriptions, die nicht zustande kommen, kein Unsubscribe. if (unsubscribe) onDestroy(unsubscribe); ist defensiver.

Erwartung: Liefert Daten zurück. onDestroy gibt nichts zurück (keine Cleanup-Cleanup). Was du in onDestroy startest, läuft danach nicht weiter — das ist der ganze Punkt.

Annahme: Läuft beim Tab-Schließen. onDestroy läuft, wenn die Komponente aus der App entfernt wird — nicht zwingend, wenn der Browser-Tab geschlossen wird. Für Tab-Schließen-Listener nutzt man window.beforeunload (mit Vorbehalten zu seiner Verlässlichkeit).

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Lifecycle

Zur Übersicht