Du hast ein SvelteKit-Projekt aufgesetzt — Zeit für die erste eigene Komponente. Dieser Artikel führt durch den vollständigen Lebensweg einer Komponente: vom leeren .svelte-File bis zur Verwendung in der Anwendung. Du lernst, wie eine Komponente aufgebaut ist, wie Reaktivität mit $state funktioniert, wie Props übergeben werden und wie das Ganze sich in das SvelteKit-Routing einfügt.

Was ist eine Komponente in Svelte?

Eine Svelte-Komponente ist eine .svelte-Datei mit bis zu drei Bereichen: <script> für Logik, das Markup als Standard-Inhalt, und optional ein <style>-Block. Komponenten werden importiert und wie ein HTML-Tag verwendet — der Tag-Name ist großgeschrieben (<Greeting />).

Drei Regeln gelten in Svelte 5:

  1. Reaktive Variablen werden mit $state(...) markiert.
  2. Props werden mit $props() ausgelesen.
  3. Komponenten-Datei und -Tag-Name beginnen mit einem Großbuchstaben.

Schritt 1 – Datei anlegen

Im SvelteKit-Skeleton wandern wiederverwendbare Komponenten in src/lib/components/. Der Alias $lib zeigt auf src/lib/ und macht Imports kurz.

text Verzeichnisstruktur
src/
  lib/
    components/
      Greeting.svelte
  routes/
    +page.svelte

Schritt 2 – Komponente schreiben

svelte src/lib/components/Greeting.svelte
<script>
    let { name = 'Welt' } = $props();
</script>

<h1>Hallo, {name}!</h1>

<style>
    h1 {
        color: teal;
        font-family: system-ui, sans-serif;
    }
</style>

Was hier passiert:

  • $props() holt die Props der Komponente. Mit Destrukturierung lässt sich ein Default setzen — ohne übergebenen Wert wird 'Welt' verwendet.
  • {name} im Markup ist der Svelte-Ausdruck zum Einbinden von JavaScript-Werten (vergleichbar mit JSX {...}).
  • <style> ist automatisch scoped: Das h1 greift nur innerhalb dieser Komponente.

Schritt 3 – Komponente verwenden

In der Startseite +page.svelte importieren und einbinden:

svelte src/routes/+page.svelte
<script>
    import Greeting from '$lib/components/Greeting.svelte';
</script>

<main>
    <Greeting name="Anna" />
    <Greeting name="Bernd" />
    <Greeting />
</main>

Die ersten beiden Verwendungen übergeben einen Namen, die dritte nutzt den Default. Der Browser zeigt drei <h1>-Elemente in Teal.

Schritt 4 – Reaktive Werte mit $state

Statische Komponenten sind langweilig. Erweitern wir das Beispiel um einen Counter:

svelte src/lib/components/Counter.svelte
<script>
    let count = $state(0);
</script>

<button onclick={() => count++}>
    Geklickt: {count}
</button>

<style>
    button {
        padding: 0.5em 1em;
        cursor: pointer;
    }
</style>

Drei Punkte zu beachten:

  • $state(0) macht count reaktiv. Eine let-Variable allein wäre in Svelte 5 nicht reaktiv.
  • count++ löst automatisch ein Re-Render aus — keine Setter-Funktion nötig.
  • onclick={...} ist Svelte-5-Syntax (nicht mehr on:click).

Schritt 5 – Werte aus Eltern empfangen und ändern

Wenn die Eltern-Komponente den State lesen und beeinflussen will, gibt es zwei übliche Wege.

Variante A: Initial-Wert übergeben, lokal halten

svelte Counter.svelte
<script>
    import { untrack } from 'svelte';

    let { initial = 0 } = $props();
    let count = $state(untrack(() => initial));
</script>

<button onclick={() => count++}>
    {count}
</button>

initial ist eine Prop, count lebt lokal — Änderungen propagieren nicht zurück. Das untrack(() => initial) ist Pflicht-Lektüre für dieses Pattern: Es macht für den Compiler explizit, dass wir den Prop-Wert nur einmalig als Seed übernehmen wollen. Ohne diesen Wrap warnt Svelte mit „state_referenced_locally”, weil unklar ist, ob die fehlende Synchronisation gewollt ist.

Variante B: Callback-Prop für Updates

svelte Counter.svelte
<script>
    let { value, onChange } = $props();
</script>

<button onclick={() => onChange?.(value + 1)}>
    {value}
</button>
svelte Verwendung
<script>
    import Counter from '$lib/components/Counter.svelte';
    let count = $state(0);
</script>

<Counter value={count} onChange={(next) => (count = next)} />

Diese Schreibweise entspricht React-style „lifted state”. Für Two-Way-Binding gibt es zusätzlich $bindable (Artikel: Bindings).

Schritt 6 – Komponente mit Children

Eine Komponente kann Inhalt zwischen ihren Tags entgegennehmen. In Svelte 5 läuft das über den Snippet-Prop children:

svelte src/lib/components/Card.svelte
<script>
    let { children } = $props();
</script>

<div class="card">
    {@render children()}
</div>

<style>
    .card {
        padding: 1rem;
        border: 1px solid #ddd;
        border-radius: 8px;
    }
</style>
svelte Verwendung
<Card>
    <h2>Profil</h2>
    <p>Anna, 30 Jahre</p>
</Card>

Mehr zu Snippets im Artikel Snippets statt Slots.

Häufige Anfänger-Fehler

Komponente kleingeschrieben verwendet. <greeting /> wird als HTML-Tag interpretiert. Immer <Greeting />.

let count = 0; ohne $state(...). Funktioniert beim ersten Render, aber Updates lösen kein Re-Render aus. In Svelte 5 ist $state Pflicht für reaktive Werte.

on:click statt onclick. Funktioniert in Svelte 5 nur im Legacy-Modus. Neuer Code nutzt direkt onclick={...}.

Vergessenes {@render children()}. Der Snippet wird nicht automatisch ausgegeben — der @render-Aufruf ist zwingend.

Default-Export oder benannter Export erwartet. .svelte-Dateien haben immer einen Default-Export, der die Komponente selbst ist. import Greeting from '...' reicht.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Grundlagen

Zur Übersicht