S SvelteKurs
M7 · State-Management

Context API

Zustand, der einen Komponenten-Teilbaum durchreicht — ohne Prop-Drilling und ohne echte Globalität.

Wozu Context?

Manchmal soll ein Wert vielen tief verschachtelten Komponenten zur Verfügung stehen, ohne ihn durch jede Zwischenebene als Prop durchzureichen („Prop-Drilling"). Genau dafür gibt es die Context API: Eine Elternkomponente legt mit setContext(key, wert) etwas ab, und jede beliebig tief liegende Nachfahr-Komponente holt es mit getContext(key) wieder heraus.

Eltern.svelte svelte
<script>
  import { setContext } from 'svelte';
  import Kind from './Kind.svelte';

  let farbe = $state('teal');

  // Nur während der Komponenten-Initialisierung aufrufbar!
  // Getter-Objekt bereitstellen -> Kinder sehen den LIVE-Wert.
  setContext('theme', {
    get farbe() { return farbe; },
    setFarbe(f) { farbe = f; }
  });
</script>

<Kind />
<button onclick={() => farbe = 'crimson'}>Ändern</button>
Kind.svelte svelte
<script>
  import { getContext } from 'svelte';

  // Holt den Wert vom nächsten Vorfahren, der ihn gesetzt hat.
  const theme = getContext('theme');
</script>

<p style="color: {theme.farbe}">Ich erbe die Farbe vom Elternteil</p>

Baum-lokal statt global

Der entscheidende Unterschied zum Modul-State der letzten Lektion: Context ist an den Komponenten-Baum gebunden. getContext findet immer den Wert des nächstgelegenen Vorfahren, der ihn gesetzt hat. Renderst du dieselbe Elternkomponente zweimal, hat jede ihren eigenen, isolierten Context — anders als ein Modul, von dem es app-weit nur eine Instanz gibt.

⚠️ Nur während der Initialisierung

setContext und getContext dürfen nur synchron während der Initialisierung einer Komponente aufgerufen werden — also im <script>-Top-Level, nicht in einem Event-Handler, Callback oder setTimeout. Danach ist der Context-Baum bereits aufgebaut.

💡 Reaktiv bleiben: Getter statt Rohwert

setContext selbst ist nicht reaktiv — es reicht einen Wert einmalig weiter. Willst du, dass Kinder Änderungen mitbekommen, legst du (wie beim Modul-State) ein Objekt mit Gettern in den Context, dessen Felder $state lesen.

Live ausprobieren

Diese Seite ruft in ihrem eigenen <script> sowohl setContext (mit einem Getter auf einen $state-Namen) als auch getContext auf. Änderst du den Namen im Feld, sieht der über Context gelesene Wert die Änderung sofort — weil ein Getter durchgereicht wurde:

▶ setContext + getContext (Getter) · Interaktiv

Über getContext gelesen: Anna

Typsichere Schlüssel

Als Schlüssel taugt jeder Wert; ein Symbol ist ideal, weil es garantiert kollisionsfrei ist. In TypeScript hinterlegst du außerdem den Typ des Werts, damit getContext nicht unknown zurückgibt:

context-keys.ts ts
// context-keys.ts — typsichere Schlüssel mit Symbol
import type { Writable } from 'svelte/store';

export interface UserCtx {
  get name(): string;
  logout(): void;
}

// Symbol als Schlüssel: garantiert kollisionsfrei
export const USER = Symbol('user');

Typische Anwendungsfälle

  • Theme für einen bestimmten Bereich (z. B. ein dunkles Panel innerhalb einer hellen Seite).
  • Formular: eine <Form>-Komponente stellt Validierungs- und Registrierungs-Funktionen bereit, die alle <Field>-Kinder nutzen.
  • i18n: die aktuelle Sprache/Übersetzungsfunktion für einen Layout-Teilbaum.
  • Komponentenbibliotheken (Tabs, Accordion), die Zustand mit ihren Unterteilen teilen.

ℹ️ Context vs. Modul-State — wann was?

Nimm Modul-State (.svelte.ts), wenn es den Wert app-weit nur einmal geben soll (Theme der ganzen App, eingeloggter Nutzer). Nimm Context, wenn derselbe „Slot" pro Teilbaum unterschiedlich belegt sein darf und der Wert nicht global sichtbar sein soll.

Einordnung zu Rails

🛤️ Rails-Vergleich: request-lokale Weitergabe

Context verhält sich wie request-lokale Instanzvariablen in Rails: Was der Controller in @article legt, steht der View und über yield/content_for auch verschachtelten Layouts zur Verfügung — aber nur für diesen einen Request-„Baum", nicht prozessweit. Genauso reicht setContext einen Wert an alles darunter durch, ohne echte Globalität.

Rails ruby
# Rails: request-lokale Instanzvariablen fließen vom Controller
# in die View und über yield/content_for in verschachtelte Layouts.
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id]) # nur für DIESEN Request-Baum
  end
end

Zusammenfassung

💡 Das Wichtigste

  • setContext(key, wert) (Eltern) und getContext(key) (Nachfahren) reichen Werte durch den Baum — ohne Prop-Drilling.
  • Beide nur während der Komponenten-Initialisierung aufrufbar.
  • Baum-lokal, nicht global: jede Eltern-Instanz hat ihren eigenen Context.
  • Für Reaktivität ein Getter-Objekt durchreichen; Symbol + Typ machen den Schlüssel sicher.

🎯 Probier es selbst

Baue eine <Tabs>-Komponente, die per setContext die aktuell aktive Tab-ID (als Getter) und eine select(id)-Funktion bereitstellt. Lass mehrere <Tab>-Kinder per getContext erkennen, ob sie aktiv sind — ganz ohne Props zwischen ihnen.