Svelte hat eine Reihe spezieller Tags mit Präfix <svelte:...>, die nicht in normales HTML passen, sondern mit dem Compiler oder Browser-Globals interagieren. Sie reichen vom Schreiben in den <head> der Seite über Window-Events bis zu Error Boundaries und rekursiven Komponenten. Dieser Artikel zeigt jeden Tag mit typischem Anwendungsfall und Code-Beispiel.
<svelte:head> – Inhalte in den HTML-<head>
Aus jeder Komponente lassen sich Inhalte direkt in den <head> der Seite schreiben — typisch für Title, Meta-Tags, Open-Graph oder Style-Imports:
<script>
let { title, description } = $props();
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
</svelte:head>Wenn mehrere Komponenten gleichzeitig in <head> schreiben, gewinnt die zuletzt gerenderte. In SvelteKit wird <svelte:head> korrekt sowohl beim SSR als auch bei Client-Navigation behandelt — Title und Meta-Tags sind immer aktuell.
<svelte:window> – Bindings und Events am Window
Direkter Zugriff auf Window-Properties und -Events ohne manuelles addEventListener:
<script>
let width = $state(0);
let height = $state(0);
let scrollY = $state(0);
function handleKey(event) {
if (event.key === 'Escape') console.log('ESC');
}
</script>
<svelte:window
bind:innerWidth={width}
bind:innerHeight={height}
bind:scrollY
onkeydown={handleKey}
/>
<p>{width} × {height}, Scroll: {scrollY}</p>Erlaubte Bindings: innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY, online, devicePixelRatio — alle reaktiv gehalten.
<svelte:document> und <svelte:body>
Analog zu Window, aber für document und <body>:
<script>
let visible = $state(true);
function handleVisibility() {
visible = !document.hidden;
}
</script>
<svelte:document onvisibilitychange={handleVisibility} />
{#if !visible}<p>Tab im Hintergrund</p>{/if}<svelte:body onclick={(e) => console.log('Body click:', e.target)} />Praktisch für Listener, die garantiert auf Document- oder Body-Ebene laufen sollen.
<svelte:self> – rekursive Komponenten
Wenn eine Komponente sich selbst innerhalb des eigenen Markups verwenden soll, ist ein direkter Selbst-Import nicht möglich. Stattdessen <svelte:self>:
<script>
let { node } = $props();
</script>
<li>
{node.label}
{#if node.children?.length}
<ul>
{#each node.children as child (child.id)}
<svelte:self node={child} />
{/each}
</ul>
{/if}
</li>Typisch für Trees, Verschachtelte Listen, Datei-Browser, Kommentar-Threads.
<svelte:options> – Compiler-Optionen pro Komponente
Mit <svelte:options> setzt man Compiler-Flags speziell für eine Komponente. Drei wichtige Anwendungsfälle:
Custom-Element
<svelte:options customElement="my-counter" />
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>{count}</button>Macht aus der Komponente ein Custom Element <my-counter>, nutzbar in jedem HTML-Kontext.
Immutable
<svelte:options immutable />Hinweis an den Compiler, dass Props nicht tief mutiert werden — erlaubt Optimierungen beim Rendern.
Accessors
<svelte:options accessors />Erzeugt Getter/Setter für alle Props auf der Komponenten-Instanz — relevant bei Custom-Elements, in Svelte-5-Reaktivität meist nicht mehr nötig.
<svelte:boundary> – Error Boundary (Svelte 5)
Eine der neuen Funktionen in Svelte 5: Fehler in einem Subtree abfangen und ein Fallback-Markup zeigen, statt die ganze App zu sprengen.
<svelte:boundary>
<UnsicheresWidget />
{#snippet failed(error, reset)}
<div class="error">
<p>Etwas ist schiefgelaufen: {error.message}</p>
<button onclick={reset}>Erneut versuchen</button>
</div>
{/snippet}
</svelte:boundary>failed ist ein Snippet mit zwei Argumenten: dem Fehler und einer Reset-Funktion, die den Boundary wieder normal rendern lässt.
Optional gibt es onerror={(error) => ...} für Logging und pending-Snippet für Suspense-ähnliches Loading-Verhalten.
<svelte:element> – dynamisches HTML-Element
Wenn der Tag-Name selbst eine Variable ist (z. B. h1 vs. h2 je nach Heading-Level), hilft <svelte:element>:
<script>
let { level = 2, children } = $props();
let tag = $derived(`h${level}`);
</script>
<svelte:element this={tag}>
{@render children()}
</svelte:element><Heading level={3}>Untertitel</Heading><svelte:element> akzeptiert die gleichen Attribute, Bindings und Events wie das normale Element.
Übersicht aller Special Elements
| Tag | Zweck |
|---|---|
<svelte:head> | Inhalte in den HTML-<head> schreiben |
<svelte:window> | Bindings und Events am window |
<svelte:document> | Bindings und Events am document |
<svelte:body> | Events am <body> |
<svelte:self> | Rekursive Verwendung der eigenen Komponente |
<svelte:options> | Compiler-Optionen pro Datei |
<svelte:boundary> | Error Boundary (Svelte 5) |
<svelte:element> | HTML-Element mit dynamischem Tag-Namen |
<svelte:component> | Dynamische Komponente (in Svelte 5 selten nötig) |
Häufige Stolperfallen
<svelte:head> mehrfach gleichzeitig.
Wenn zwei Komponenten gleichzeitig denselben Title schreiben, gewinnt die zuletzt gerenderte. In SvelteKit ist die übliche Konvention: <svelte:head> nur in Routen-Komponenten, nicht in Widgets.
<svelte:window> und <svelte:document> im SSR.
Beide werden beim Server-Rendering einfach übersprungen, da kein window/document existiert. Innerhalb von $effect sind sie sicher.
<svelte:self> ohne Rekursions-Abbruch.
Klassiker: Endlose Rekursion mit „Maximum call stack size exceeded”. Eine Abbruch-Bedingung ist Pflicht.
<svelte:boundary> als App-Wrapper.
Eine Boundary auf App-Ebene fängt zwar alle Fehler ab, blockiert aber Hot-Reload-Diagnostik in Development. Lieber gezielt um spezifische, fehleranfällige Bereiche legen.