Apps need state that many components read: sessions, carts, themes. Svelte 5 needs no separate store library for it.
Runes work in `.svelte.js` / `.svelte.ts` modules, so shared state can live outside components:
// cart.svelte.js
export const cart = $state({ items: [] });
export function add(item) { cart.items.push(item); }Any component importing cart sees the same reactive object. One rule: you can't export a `$state` variable that you reassign (export let count = $state(0) then count++ elsewhere won't propagate), because importers receive the value, not the binding. Export an object and mutate its properties, or export getter functions.
Classes are a clean way to bundle state with behaviour: fields declared with $state/$derived become reactive accessors.
Module-level state is a singleton per process. In a server-rendered app that means it is shared between all users' requests, so never keep per-user data there. Use context instead: setContext(key, value) in a parent during initialisation and getContext(key) in any descendant. Context is scoped to the component tree, so on the server each request gets its own. Svelte 5.40+ adds `createContext()`, which returns typed get and set functions (plus a has check) and avoids string keys.
Put a $state object (or class instance) in context so descendants see updates. Svelte stores (writable, $store syntax) still work and remain useful for streams of values, but runes cover most shared-state needs.
Example
// counter.svelte.js
export class Counter {
count = $state(0);
double = $derived(this.count * 2);
increment = () => { this.count++; }; // arrow keeps `this` when passed around
}
export const globalCounter = new Counter(); // singleton (careful with SSR)
// session.js - per-tree state via context (Svelte 5.40+)
import { createContext } from 'svelte';
export const [getSession, setSession] = createContext();
// Layout.svelte <script>
// import { setSession } from './session.js';
// let { user, children } = $props();
// setSession({ get user() { return user; } }); // stays reactive
// Avatar.svelte <script> (any depth below)
// import { getSession } from './session.js';
// import { globalCounter } from './counter.svelte.js';
// const session = getSession();
// <button onclick={globalCounter.increment}>{session.user.name} {globalCounter.double}</button>Remember
- Runes in
.svelte.jsmodules create shared reactive state; export objects, not reassigned variables - Classes with
$statefields bundle state and methods - Use context (
setContext/getContextorcreateContext) for per-tree and SSR-safe state
Watch out for
- Keeping per-user data in module-level state in an SSR app, leaking it between requests.