tick() ist eine kleine, aber sehr nützliche Funktion. Sie gibt dir die Möglichkeit, mit await darauf zu warten, dass anstehende DOM-Updates abgeschlossen sind. Das klingt erst mal abstrakt — wird aber sofort einleuchtend, sobald man das typische Problem versteht: Ich ändere einen State, das DOM hat sich noch nicht aktualisiert, und ich versuche schon, mit dem neuen DOM zu arbeiten. tick() löst genau das.

Das Problem, das tick() löst

Stell dir vor, du klickst einen Button. Der Klick zeigt ein neues Eingabefeld an, das du sofort fokussieren willst:

svelte − Funktioniert nicht zuverlässig
<script>
    let isEditing = $state(false);
    let inputElement;

    function startEdit() {
        isEditing = true;
        inputElement.focus(); // Fehler: inputElement existiert noch nicht im DOM
    }
</script>

<button onclick={startEdit}>Bearbeiten</button>

{#if isEditing}
    <input bind:this={inputElement} />
{/if}

Was hier schiefgeht: Wenn isEditing = true gesetzt wird, plant Svelte einen Re-Render. Der Re-Render selbst passiert aber danach — als Microtask. In der nächsten Zeile (inputElement.focus()) ist das <input> noch gar nicht im DOM. inputElement ist undefined (oder das alte Element).

Die Lösung: await tick()

tick() gibt dir ein Promise zurück, das erst auflöst, nachdem Svelte alle anstehenden Updates ans DOM geschrieben hat. Mit await tick() wartest du also genau auf diesen Moment:

svelte + Funktioniert zuverlässig
<script>
    import { tick } from 'svelte';

    let isEditing = $state(false);
    let inputElement;

    async function startEdit() {
        isEditing = true;
        await tick();          // jetzt ist das <input> im DOM
        inputElement.focus();  // funktioniert
    }
</script>

<button onclick={startEdit}>Bearbeiten</button>

{#if isEditing}
    <input bind:this={inputElement} />
{/if}

Schritt für Schritt:

  1. isEditing = true markiert ein Re-Render als „ausstehend”.
  2. await tick() pausiert die Funktion, bis das Re-Render fertig ist.
  3. Nach await tick() ist inputElement belegt — der Fokus klappt.

Was bedeutet „pending state changes”?

Svelte führt Reaktivitäts-Updates gebatcht aus. Wenn du in einer synchronen Funktion mehrmals State änderst, gibt es einen Update-Durchgang am Ende, nicht einen pro Zuweisung.

svelte Mehrere Updates, ein Render
<script>
    let count = $state(0);
    let label = $state('');

    function bump() {
        count++;
        label = `Wert: ${count}`;
        count++;
        label = `Wert: ${count}`;
        // -> Svelte rendert genau einmal nach Funktionsende
    }
</script>

tick() wartet, bis alle dieser geplanten Renders durchlaufen sind und das DOM den neuen Stand hat.

Praxis-Beispiel: Scroll-Position nach DOM-Update

Ein zweiter typischer Fall: Du fügst einer Liste am Ende ein Element hinzu und willst dann zu diesem Element scrollen.

svelte Scroll-zu-letztem-Element
<script>
    import { tick } from 'svelte';

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

    async function addMessage(text) {
        messages.push({ id: Date.now(), text });
        await tick();
        listEnd?.scrollIntoView({ behavior: 'smooth' });
    }
</script>

<ul>
    {#each messages as msg (msg.id)}
        <li>{msg.text}</li>
    {/each}
    <div bind:this={listEnd}></div>
</ul>

<button onclick={() => addMessage('Hallo')}>Nachricht</button>

Ohne await tick() würde scrollIntoView auf das alte letzte Element zeigen — die neue Nachricht ist noch nicht gerendert. Mit await tick() ist sie da.

Praxis-Beispiel: Element messen nach Update

svelte Höhe nach Inhalts-Wechsel messen
<script>
    import { tick } from 'svelte';

    let text = $state('Kurz');
    let element;
    let height = $state(0);

    async function update() {
        text = 'Ein deutlich längerer Text, der die Höhe verändert …';
        await tick();
        height = element.offsetHeight;
    }
</script>

<div bind:this={element}>{text}</div>
<button onclick={update}>Update</button>

<p>Höhe nach Update: {height}px</p>

Vorher hätte element.offsetHeight noch den alten Wert geliefert, weil der DOM-Knoten nicht neu gemessen war.

Wann brauchst du tick() nicht?

tick() ist die Antwort auf die spezifische Frage „wie warte ich auf ein DOM-Update”. Für die meisten typischen Updates (Re-Render, Listen-Aktualisierung, Klassen-Wechsel) brauchst du nichts zu tun — Svelte macht das von selbst.

Du brauchst tick() nur, wenn du nach einer State-Änderung direkt mit dem aktualisierten DOM arbeiten willst:

  • Fokussieren neu eingeblendeter Elemente
  • Scrollen nach Inhalts-Änderung
  • Messen von Größen/Positionen
  • Drittanbieter-Library mit aktuellem DOM-Stand neu initialisieren

In allen anderen Fällen ist tick() overkill.

Alternative: $effect mit den richtigen Abhängigkeiten

In Svelte 5 lässt sich vieles eleganter mit $effect lösen, weil Effects automatisch nach dem DOM-Update laufen:

svelte Mit $effect statt tick()
<script>
    let isEditing = $state(false);
    let inputElement;

    $effect(() => {
        if (isEditing && inputElement) {
            inputElement.focus();
        }
    });
</script>

<button onclick={() => isEditing = true}>Bearbeiten</button>

{#if isEditing}
    <input bind:this={inputElement} />
{/if}

Hier wartet $effect automatisch auf das DOM-Update — kein tick() nötig. Welche Variante du nimmst, ist Geschmackssache:

  • await tick() in einer Event-Handler-Funktion ist sehr lokal und linear lesbar.
  • $effect ist deklarativer und reagiert auf jede zukünftige Änderung der Abhängigkeit.

Häufige Stolperfallen

tick() ohne await. tick() gibt ein Promise zurück. Ohne await wartest du nicht — der nächste Befehl läuft sofort.

svelte − Hilft nicht
isEditing = true;
tick();             // Promise wird ignoriert
inputElement.focus(); // immer noch zu früh

tick() außerhalb eines async aufrufen. await braucht eine async-Funktion. Wenn dein Handler nicht async ist, geht await tick() nicht.

Erwarten, dass tick() mehrere Render-Phasen abwartet. tick() löst auf, sobald die aktuell ausstehenden Updates angewendet sind. Wenn dein Code danach noch State ändert, brauchst du ein zweites await tick().

tick() im SSR. Funktioniert beim Server-Side-Rendering nicht im klassischen Sinn — dort gibt es kein „nächstes Frame”. In der Regel brauchst du tick() aber nur im Browser.

tick() als Performance-Tuning verwenden. tick() macht den Code nicht schneller — eher langsamer, weil es eine Microtask-Wartezeit einfügt. Verwenden nur, wenn nötig.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Lifecycle

Zur Übersicht