patternjavascriptsvelteModerate
Svelte stores: writable, readable, and derived
Viewed 0 times
Svelte 3/4
svelte storeswritablederivedreadableauto-subscribe$ prefixstate management
Problem
Sharing state across unrelated Svelte components without prop drilling or context. Not using derived stores leads to duplicated subscription logic and stale values.
Solution
Use the svelte/store primitives:
// stores/user.ts
import { writable, derived, readable } from 'svelte/store';
// writable: mutable shared state
export const user = writable<User | null>(null);
export const theme = writable('light');
// readable: external data source (no .set from outside)
export const time = readable(new Date(), (set) => {
const interval = setInterval(() => set(new Date()), 1000);
return () => clearInterval(interval); // cleanup
});
// derived: computed from other stores
export const isLoggedIn = derived(user, $user => $user !== null);
export const greeting = derived(
[user, time],
([$user, $time]) =>
);
// In a component — $ prefix auto-subscribes and unsubscribes
<script>
import { user, greeting } from '../stores/user';
</script>
<p>{$greeting}</p>
<button on:click={() => user.set(null)}>Logout</button>
// stores/user.ts
import { writable, derived, readable } from 'svelte/store';
// writable: mutable shared state
export const user = writable<User | null>(null);
export const theme = writable('light');
// readable: external data source (no .set from outside)
export const time = readable(new Date(), (set) => {
const interval = setInterval(() => set(new Date()), 1000);
return () => clearInterval(interval); // cleanup
});
// derived: computed from other stores
export const isLoggedIn = derived(user, $user => $user !== null);
export const greeting = derived(
[user, time],
([$user, $time]) =>
Hi ${$user?.name}, it's ${$time.toTimeString()});
// In a component — $ prefix auto-subscribes and unsubscribes
<script>
import { user, greeting } from '../stores/user';
</script>
<p>{$greeting}</p>
<button on:click={() => user.set(null)}>Logout</button>
Why
Svelte stores implement a simple contract: subscribe(), set(), update(). The $ prefix in a component is compiler sugar that subscribes on mount and unsubscribes on destroy automatically. Derived stores recalculate lazily when subscribed.
Gotchas
- The $ prefix only works inside .svelte files — in .ts files, manually subscribe/unsubscribe
- Subscribing in .ts without cleanup leaks memory — always return the unsubscribe function
- derived() is lazy — it only recalculates when there's at least one subscriber
- store.update(fn) is atomic — use it instead of set when new value depends on old value
Code Snippets
Using stores in non-Svelte files
// Manual subscribe in .ts (not .svelte)
import { get } from 'svelte/store';
// Snapshot value without subscribing
const currentUser = get(user);
// Subscribe manually
const unsubscribe = user.subscribe($user => {
console.log($user);
});
// Later:
unsubscribe();Context
When sharing state across Svelte components with stores
Revisions (0)
No revisions yet.