The Store Contract
A store is just a contract
Section titled “A store is just a contract”There is nothing magic about a Svelte store. A store is any object with a subscribe method that takes a callback and calls it with the current value, immediately and on every change — returning an unsubscribe function. That’s the whole contract:
// The minimal store contract, written by hand.function createCounter() { let value = 0; const subscribers = new Set();
return { subscribe(fn) { fn(value); // 1. call immediately with the current value subscribers.add(fn); return () => subscribers.delete(fn); // 2. return an unsubscribe function }, increment() { value += 1; subscribers.forEach((fn) => fn(value)); // 3. notify on change }, };}Because the contract is this small, anything that implements it is a store — including RxJS observables (which also have a compatible subscribe). That interoperability is the reason Svelte defined a contract instead of a concrete class.
The $ prefix auto-subscribes
Section titled “The $ prefix auto-subscribes”Writing store.subscribe(...) by hand in every component would be tedious and leak-prone. Inside a component, Svelte gives you the $store shorthand: prefix a store with $ and Svelte subscribes for you, gives you the current value, re-renders when it changes, and unsubscribes automatically when the component is destroyed.
<script> import { counter } from './counter.js';</script>
<!-- $counter is the current value; the component re-renders on every change --><button onclick={counter.increment}>count is {$counter}</button>flowchart LR prefix["$store in a component"] --> sub["subscribe on mount"] sub --> val["read the current value"] val --> rerender["re-render on change"] rerender --> unsub["auto-unsubscribe on destroy"]
You can also assign to $store ($counter = 5) if the store is writable — Svelte compiles that to calling the store’s set. The $ prefix only works inside .svelte files and .svelte.js/.svelte.ts modules, because it’s compiler magic, not a runtime function.
Why the contract still matters in Svelte 5
Section titled “Why the contract still matters in Svelte 5”Even though runes now cover most shared-state needs (next lessons), the store contract remains valuable:
- Interop. Any observable-like source with a compatible
subscribeworks with$— RxJS, custom event streams, third-party libraries. - Async sources.
readable(next lesson) wraps a push-based source (a WebSocket, a geolocation watcher) behind the same simple contract. - Existing code. Millions of lines of Svelte use stores; understanding the contract lets you read and maintain them.