Listen rendern Svelte-Templates mit dem {#each}-Block. Er sieht klassischer aus als das React-map()-Pattern, ist aber kompakter und bietet eingebaute Features wie Keyed Iteration und einen {:else}-Fallback bei leeren Arrays. Dieser Artikel zeigt alle Varianten und erklärt, warum Keys bei dynamischen Listen entscheidend sind.

Grundsyntax

svelte Liste rendern
<script>
    let users = $state([
        { id: 1, name: 'Anna' },
        { id: 2, name: 'Bernd' },
        { id: 3, name: 'Carla' },
    ]);
</script>

<ul>
    {#each users as user}
        <li>{user.name}</li>
    {/each}
</ul>

{#each items as item} öffnet den Block, {/each} schließt ihn. Die einzelne Iteration rendert das Markup einmal pro Element.

Index zugreifen

Ein zweiter Parameter liefert den Index:

svelte Mit Index
<ol>
    {#each users as user, index}
        <li>{index + 1}. {user.name}</li>
    {/each}
</ol>

Keyed Each Blocks

Bei dynamischen Listen — Hinzufügen, Entfernen oder Umsortieren — ist eine Identität pro Element wichtig. Sonst weiß Svelte beim Diffing nicht, welches alte Element zu welchem neuen gehört, und behandelt verschobene Elemente fälschlich als „verändert”.

svelte Keyed
<ul>
    {#each users as user (user.id)}
        <li>{user.name}</li>
    {/each}
</ul>

Der Ausdruck in Klammern ((user.id)) ist der Key. Er muss eindeutig pro Element sein und stabil zwischen Renders.

Warum Keys wichtig sind

Stell dir eine TodoList mit Checkbox-State pro Eintrag vor:

svelte − Ohne Key
{#each todos as todo}
    <li>
        <input type="checkbox" />
        {todo.text}
    </li>
{/each}

Wenn ein Eintrag oben entfernt wird, verschieben sich Index und DOM-Position. Ohne Key denkt Svelte: „Erster <li> hat sich nur im Text geändert.” Der Checkbox-State (lokales DOM-State) bleibt am alten Index hängen — und ist falsch zugeordnet.

svelte + Mit Key
{#each todos as todo (todo.id)}
    <li>
        <input type="checkbox" />
        {todo.text}
    </li>
{/each}

Mit Key erkennt Svelte: „Eintrag mit ID 5 ist weg” — und zerstört genau diesen <li>-Eintrag. Der State der anderen bleibt korrekt erhalten.

{:else} – Fallback bei leerer Liste

Eingebauter Fallback für leere Arrays:

svelte Leere-Liste-Fallback
{#each todos as todo (todo.id)}
    <li>{todo.text}</li>
{:else}
    <li>Keine Aufgaben vorhanden.</li>
{/each}

Spart das {#if todos.length}...{:else}...{/if}-Konstrukt um den {#each} herum.

Destrukturierung

Item-Felder lassen sich direkt destrukturieren:

svelte Destrukturierung
{#each users as { id, name, role } (id)}
    <li>
        <strong>{name}</strong> ({role})
    </li>
{/each}

Auch Array-Destrukturierung funktioniert:

svelte Tuple-Destrukturierung
{#each entries as [key, value] (key)}
    <dt>{key}</dt>
    <dd>{value}</dd>
{/each}

Über Iterables, nicht nur Arrays

{#each} iteriert über alles, was in einem for...of funktioniert: Arrays, Map, Set, Generatoren.

svelte Map iterieren
<script>
    const settings = new Map([
        ['theme', 'dark'],
        ['locale', 'de'],
    ]);
</script>

<dl>
    {#each settings as [key, value] (key)}
        <dt>{key}</dt>
        <dd>{value}</dd>
    {/each}
</dl>

Bei reaktiven Maps oder Sets aus svelte/reactivity (SvelteMap, SvelteSet) reagiert die Liste automatisch auf Änderungen.

Verschachtelte each-Blöcke

svelte Tabelle
<table>
    {#each rows as row (row.id)}
        <tr>
            {#each row.cells as cell, index (index)}
                <td>{cell}</td>
            {/each}
        </tr>
    {/each}
</table>

Beachten: Bei verschachtelten Schleifen ist jeder Key nur innerhalb seines eigenen Blocks eindeutig. Du brauchst nicht App-weit eindeutige IDs.

Häufige Stolperfallen

Index als Key bei dynamischen Listen.

svelte − Index als Key
{#each items as item, i (i)}
    ...
{/each}

Funktioniert nur bei statischen Listen. Bei Insert/Delete in der Mitte verhält sich das wie „kein Key” — Identität geht verloren.

Zufalls-Key zur Render-Zeit.

svelte − Random Key
{#each items as item (Math.random())}

Bei jedem Render bekommt jeder Eintrag einen neuen Key — Svelte unmounted und remounted alle Items. Teuer und zerstört State.

Mutationen ohne Reaktivität. Bei nicht-reaktiven Arrays (kein $state) ändert sich die Liste zwar im Speicher, aber Svelte rendert nicht neu. Liste muss reaktiv sein.

Schreibzugriffe auf das Iterations-Item. {#each items as item} — das item ist ein normaler Loop-Bezeichner, kein bind-bares Ziel. Wenn du das Item ändern willst, greife per Index oder ID auf das Original-Array zu.

Vergessenes {/each}. Compiler-Fehler. Wie bei {#if} müssen Blöcke explizit geschlossen werden.

Praxis-Beispiel: Filter-Tabelle

svelte Filterable-List
<script>
    let users = $state([...]);
    let query = $state('');

    let filtered = $derived(
        users.filter((u) =>
            u.name.toLowerCase().includes(query.toLowerCase())
        )
    );
</script>

<input bind:value={query} placeholder="Suchen..." />

<ul>
    {#each filtered as user (user.id)}
        <li>{user.name}</li>
    {:else}
        <li>Keine Treffer.</li>
    {/each}
</ul>

filtered ist ein $derived — bei jeder Eingabe neu berechnet, der {#each} reagiert sofort.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Template-Syntax

Zur Übersicht