Stores sind Sveltes klassischer Mechanismus, um reaktive Werte außerhalb von Komponenten zu halten. Bevor es Runes gab, waren sie der einzige saubere Weg, gemeinsame Daten zwischen mehreren Komponenten zu teilen. In Svelte 5 mit $state sind sie nicht mehr für jeden Fall nötig — aber an einigen Stellen weiterhin die elegantere Wahl. Dieser Artikel erklärt, was Stores sind, wie sie funktionieren und wann du sie heute noch verwenden solltest.

Was ist ein Store?

Ein Store ist ein Objekt, das einen Wert hält und anderen Bescheid gibt, sobald sich dieser Wert ändert. Du kannst dir das wie einen kleinen Sender vorstellen: Wer „abonniert”, bekommt jede neue Sendung mit.

Praktisch gesehen ist ein Store ein Objekt mit (mindestens) einer subscribe-Methode:

ts Konzeptueller Store
const store = {
    subscribe(callback) {
        // Ruft callback sofort und bei jeder Änderung auf
        return () => {
            // Unsubscribe-Funktion
        };
    },
};

Das ist der sogenannte Store-Vertrag: subscribe(fn) ruft fn mit dem aktuellen Wert auf — sofort und bei jeder weiteren Änderung — und gibt eine Funktion zurück, mit der man die Subscription wieder beendet.

Wenn der Store auch von außen schreibbar sein soll, hat er zusätzlich set und update.

Eingebaute Stores aus svelte/store

Statt jeden Store von Hand zu bauen, gibt es drei fertige Helfer im Modul svelte/store:

HelferZweck
writable()Schreibbarer Store mit set und update.
readable()Nur lesbarer Store. Werte werden intern aktualisiert.
derived()Abgeleiteter Store, berechnet sich aus einem oder mehreren Stores.
ts Erster writable
import { writable } from 'svelte/store';

export const count = writable(0);

count ist ab jetzt ein Store mit dem Anfangswert 0. Ihn benutzen kann jede Komponente, die ihn importiert.

Die $-Syntax in Komponenten

Manuell mit subscribe/unsubscribe zu arbeiten ist umständlich. Svelte hat dafür eine Kurzschreibweise: das $-Präfix.

svelte Counter.svelte
<script>
    import { count } from '$lib/stores';
</script>

<p>Aktueller Wert: {$count}</p>

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

Was hier vor sich geht:

  • Lesen: $count gibt den aktuellen Wert des Stores zurück. Svelte abonniert den Store automatisch im Hintergrund — und kündigt die Subscription beim Unmount der Komponente.
  • Schreiben: $count = 5 ruft intern count.set(5) auf. $count++ macht dasselbe wie count.update(n => n + 1).

So fühlt sich ein Store in der Komponente an wie eine ganz normale Variable.

Store-Wert außerhalb von Komponenten lesen

In Komponenten ist $store ideal. Außerhalb (in .ts-Helpern, in API-Funktionen) gibt es get():

ts get() in einem Helper
import { get } from 'svelte/store';
import { token } from '$lib/stores/auth';

export async function fetchProtected() {
    return fetch('/api/me', {
        headers: { Authorization: `Bearer ${get(token)}` },
    });
}

get(store) liefert einen einmaligen Snapshot des aktuellen Werts. Wichtig: Das ist nicht reaktiv. Wenn token sich ändert, wird fetchProtected nicht erneut aufgerufen — get() liest nur einmal.

In hochfrequentem Code (z. B. Tausende Aufrufe pro Sekunde) ist get() etwas weniger performant als ein abonnierter Wert. In normalen Apps unbemerkbar.

Wann Stores, wann $state? Die Svelte-5-Frage

Mit Svelte 5 und Runes hat sich die Lage verschoben. $state deckt heute viele Fälle ab, für die man früher Stores genutzt hat. Aber Stores sind nicht obsolet.

$state ist die natürlichere Wahl, wenn …

  • Der Wert in einer einzelnen Komponente lebt.
  • Der Wert in eng verwandten Komponenten geteilt wird (Eltern -> Kind via Props oder Context).
  • Du objektorientierte Modelle baust, die Daten und Methoden bündeln (Klassen mit $state-Properties).
  • Du keinen RxJS-ähnlichen Kontroll-Mechanismus brauchst.

Stores bleiben sinnvoll, wenn …

  • Eine Bibliothek etwas exponiert, das auch in Nicht-Svelte-Code funktionieren soll.
  • Du eine subscribe/unsubscribe-API brauchst (z. B. für Integration mit RxJS, externen Frameworks, Tests).
  • Mehrere unabhängige Module ohne Komponenten-Hierarchie auf denselben Wert zugreifen sollen.
  • Du Werte aus asynchronen Quellen modellieren willst (Stream, WebSocket-Daten), bei denen derived() mit set-Funktion gut passt.

In modernem Svelte-5-Code wirst du oft eine Mischung sehen: globaler Auth-State per $state in einer .svelte.ts-Datei, aber ein derived-Store für etwas, das in einer Bibliothek angeboten wird.

Erstes vollständiges Beispiel

Ein Counter, in einer eigenen Datei definiert und von zwei Komponenten gleichzeitig genutzt.

ts src/lib/stores/counter.ts
import { writable } from 'svelte/store';

export const count = writable(0);
svelte src/lib/components/CountButton.svelte
<script>
    import { count } from '$lib/stores/counter';
</script>

<button onclick={() => $count++}>+1</button>
svelte src/lib/components/CountDisplay.svelte
<script>
    import { count } from '$lib/stores/counter';
</script>

<p>Wert ist: {$count}</p>

Die beiden Komponenten kennen sich nicht. Trotzdem zeigen beide immer denselben Wert — weil sie denselben Store verwenden.

Stores werden automatisch entkoppelt

Wenn eine Komponente, die einen Store per $ nutzt, unmounted wird, kündigt Svelte das Abonnement automatisch. Du musst dich um Cleanup nicht kümmern. Das ist der wahrscheinlich größte Komfort der $-Syntax — ohne sie müsste man subscribe/unsubscribe per Hand verwalten.

Häufige Stolperfallen

$store außerhalb einer .svelte-Datei. Funktioniert nicht. Die $-Syntax ist Compile-Zeit-Magie und nur in .svelte- und .svelte.ts-Dateien verfügbar. In .ts-Helpern: get(store) oder manuelles store.subscribe(...).

$store = neuerWert mit unschreibbarem Store. readable und derived haben kein set — der Schreibversuch schlägt fehl. Nur writable ist beschreibbar.

Store nur in <script> lesen.

svelte − Wert wird nicht aktualisiert
<script>
    import { count } from '$lib/stores/counter';
    const value = $count; // einmalig kopiert
</script>

<p>{value}</p>

value wird einmal beim Setup kopiert und ist danach nicht mehr reaktiv. Direkt im Markup {$count} schreiben.

$store in einer Funktion außerhalb des Komponenten-Setup. Module-Code-Helfer können $store nicht nutzen. Dort get(store) oder eine Methode aus einem Custom-Store anbieten.

Erwarten, dass Stores beim Page-Reload erhalten bleiben. Stores leben im Speicher. Bei einem Reload werden sie neu erzeugt — mit ihrem Initialwert. Für persistenten State: localStorage in einem Custom Store integrieren (siehe Custom Stores).

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Stores

Zur Übersicht