Svelte Store Contract: subscribe() & set() with $ Auto-Subscription
- Last updated
- Prerequisites:
- Svelte basics and reactivity concepts
- Understanding of subscriptions and unsubscribing
- svelte
- stores
- subscribe
- contract
- reactivity
- writable
Read at your depth
The practical view
A Svelte store is any object with a subscribe method: { subscribe(callback): () => void }. That's the contract. writable() creates a store with set() and update(): const count = writable(0); count.set(1); count.update(n => n + 1);. To read a store's value in a template use the $ prefix: {$count}. The $ prefix auto-subscribes in templates and in .svelte files (it compiles to subscribe/unsubscribe). Outside components (plain .ts), call count.subscribe(v => ...) and keep the returned unsubscribe function. readable(), derived() create read-only and derived stores.
The same idea in other frameworks
Context + useSyncExternalStore is the React analogue of the subscribe contract.BehaviorSubject/Observable + async pipe mirror the subscribe-and-render model.Vue's ref() + provide/inject covers the same global-shared-state role.Legacy vs modern
Manual subscribe/unsubscribe vs the $ auto-subscription
In plain JS you must subscribe and call the returned unsubscribe; in Svelte templates the $ prefix handles the full lifecycle automatically.
const unsub = count.subscribe(v => { console.log(v); }); // ❌ OLD: must remember to unsubscribe<p>{$count}</p> // ✅ NEW: compiler handles subscribe + unsubscribe lifecycleInterview gotchas
Context
This tests whether you understand the minimal interop contract — interviewers want to confirm you know that ANY object with subscribe is a store, which is what makes stores extensible and testable.
The mechanical answer
The store contract is: an object with a subscribe method taking a callback and returning an unsubscribe function. That's it. writable/readable/derived are built-in implementations, but any object satisfying the contract is a valid store — which means third-party stores, mocks, and even streams wrapped with a subscribe method interoperate seamlessly with Svelte's $ sugar. The subscribe callback receives the current value immediately upon subscription (synchronous initial emission), then on every subsequent change. The returned unsubscribe function must remove the subscriber. This minimal contract is what allows Svelte to treat stores uniformly: the compiler's $ transformation only depends on subscribe existing.
Trap
The naive answer is 'a store is a writable object'. The contract is just subscribe; writable adds set/update on top. Another trap: assuming subscription is lazy — the callback fires immediately with the current value on subscribe, so code must handle that initial emission. Candidates who can define the contract precisely and mention the synchronous initial call show real understanding.
Context
This probes the migration story — interviewers want to know the conceptual overlap and the guidance for choosing between the two state primitives in modern Svelte.
The mechanical answer
Stores (subscribe/set) are an observer-pattern primitive with a minimal interop contract; runes ($state, $derived) are compiler-backed signals integrated directly into the reactive graph. In Svelte 5, runes are the recommended default for component state: they are simpler (no subscribe/unsubscribe, no $ prefix), more granular, and avoid the wrapper object. Stores remain valuable for module-level shared state that must be consumed from plain TypeScript, for interop with libraries expecting the subscribe contract, and in codebases not yet migrated. The migration path converts writable(0) to $state(0) and $store to the rune syntax; the compiler keeps supporting the $ store prefix for compatibility. Both work side by side within one app.
Trap
The naive answer is 'runes replace stores entirely'. Stores are still supported and still the right tool for module-scope shared state consumed outside components. Another trap: mixing the $ store prefix with runes in confusing ways — the $ prefix still refers to stores, while runes use $state/$derived. Candidates who articulate the coexistence and the module-state use case show real migration experience.