Snippets sind Svelte 5s Antwort auf Slots — und in fast jeder Hinsicht mächtiger. Sie sind benannt, können Argumente entgegennehmen, sind als TypeScript-Type abbildbar, lassen sich aus dem <script module>-Bereich exportieren und werden ganz normal als Props weitergereicht. Dieser Artikel zeigt die Syntax ({#snippet} und {@render}), den impliziten children-Prop, named Snippets, Snippets mit Parametern und die Migration aus Svelte 4.
Was ist ein Snippet?
Ein Snippet ist ein wiederverwendbares Stück Markup, das mit {#snippet name()} ... {/snippet} definiert und mit {@render name()} an einer beliebigen Stelle gerendert wird.
<script>
let users = [{ name: 'Anna' }, { name: 'Bernd' }];
</script>
{#snippet userBadge(user)}
<span class="badge">{user.name}</span>
{/snippet}
{#each users as user}
{@render userBadge(user)}
{/each}
<style>
.badge {
padding: 4px 8px;
border-radius: 999px;
background: #eef;
}
</style>Snippets sind lokal sichtbar im umschließenden Scope — sie können sich selbst, einander und Variablen aus dem Scope referenzieren.
Implizite children-Prop
Inhalte zwischen den Tags einer Komponente werden automatisch zur Snippet-Prop children:
<script>
let { children } = $props();
</script>
<article class="card">
{@render children()}
</article><Card>
<h2>Profil</h2>
<p>Anna, 30 Jahre</p>
</Card>Der Inhalt zwischen <Card> und </Card> landet in children und wird per {@render children()} ausgegeben.
Named Snippets als Props
Mehrere Inhaltsbereiche werden über benannte Snippets gelöst:
<script>
let { header, footer, children } = $props();
</script>
<article>
{#if header}
<header>{@render header()}</header>
{/if}
<div class="body">
{@render children()}
</div>
{#if footer}
<footer>{@render footer()}</footer>
{/if}
</article><Article>
{#snippet header()}
<h1>Titel</h1>
{/snippet}
<p>Hauptinhalt — landet automatisch in children.</p>
{#snippet footer()}
<small>Autor: Anna</small>
{/snippet}
</Article>Der Eltern-Komponent definiert die Snippets innerhalb der Komponenten-Tags — sie werden vom Compiler an die entsprechenden Props gebunden.
Snippets mit Argumenten
Anders als Slots können Snippets Parameter annehmen — perfekt für Listen, Tabellen oder generische Render-Patterns:
<script>
let { items, item } = $props();
</script>
<ul>
{#each items as element, index}
<li>
{@render item(element, index)}
</li>
{/each}
</ul><List items={users}>
{#snippet item(user, index)}
<strong>#{index + 1}</strong>
{user.name}
{/snippet}
</List>Das ersetzt das Svelte-4-Konstrukt <slot {user} /> und <List let:user>{user.name}</List> mit einer expliziteren Funktions-artigen Schreibweise.
TypeScript-Typen für Snippets
Snippet aus dem svelte-Modul:
<script lang="ts">
import type { Snippet } from 'svelte';
type Props = {
children: Snippet;
header?: Snippet;
};
let { children, header }: Props = $props();
</script><script lang="ts">
import type { Snippet } from 'svelte';
type Item = { id: string; name: string };
type Props = {
items: Item[];
item: Snippet<[Item, number]>;
};
let { items, item }: Props = $props();
</script>Snippet<[Type1, Type2, ...]> definiert eine Tupel der Argumente, die das Snippet beim Aufruf erwartet.
Snippets exportieren
Aus <script module> lassen sich Snippets auch als Modul-Export anbieten — sehr praktisch für UI-Bibliotheken:
<script module>
export { iconHome, iconUser };
</script>
{#snippet iconHome()}
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M3 12L12 3l9 9..." />
</svg>
{/snippet}
{#snippet iconUser()}
<svg viewBox="0 0 24 24" width="24" height="24">
<circle cx="12" cy="8" r="4" />
</svg>
{/snippet}<script>
import { iconHome, iconUser } from '$lib/icons.svelte';
</script>
{@render iconHome()}
{@render iconUser()}Das ist gleichzeitig eine simple, performante Alternative zu Icon-Komponenten-Libraries.
Snippets als normale Werte
Snippets sind in Svelte 5 first-class — sie lassen sich in Variablen speichern und an mehrere Stellen weitergeben:
<script>
let { variant } = $props();
</script>
{#snippet primary()}<span class="primary">!</span>{/snippet}
{#snippet danger()}<span class="danger">!</span>{/snippet}
{@render (variant === 'danger' ? danger : primary)()}Einsatz typischerweise in Render-Logik, die abhängig vom State unterschiedliche Snippets verwendet.
Migration aus Svelte 4
| Svelte 4 | Svelte 5 |
|---|---|
<slot /> | {@render children()} mit let { children } = $props(); |
<slot name="header" /> | {@render header()} mit named Snippet-Prop |
<slot {value} /> + let:value | Snippet mit Argument |
<svelte:fragment slot="x"> | {#snippet x()}...{/snippet} |
$$slots.header | {#if header}...{/if} (Snippet-Prop existiert oder nicht) |
Häufige Stolperfallen
Snippet-Aufruf vergessen.
{children} allein rendert das Snippet nicht — es muss {@render children()} heißen.
Optional-Chaining beim Aufruf.
Bei optionalen Snippet-Props prüfen oder Optional-Chaining nutzen: {@render header?.()} oder {#if header}{@render header()}{/if}.
Snippet außerhalb seines Scopes verwenden. Snippets sind lexikalisch — sie sehen den umschließenden Scope. Ein Snippet aus Komponente A lässt sich nicht in Komponente B verwenden, ohne als Prop weiterzureichen.
Reservierte Argument-Namen. Snippet-Parameter funktionieren wie normale Funktionsparameter. Destrukturierung und Defaults sind erlaubt, Rest-Parameter dagegen nicht.
Slot/Snippet-Mix in Bestandscode. Im Legacy-Modus laufen Slots weiter. In einer Runes-Komponente mischst du sie nicht — entweder ganz auf Snippets umstellen oder Komponente vorerst im Legacy-Modus belassen.