name: svelte-state-context description: 'Svelte 5 context API (createContext, setContext/getContext), lifecycle hooks (onMount, onDestroy, tick, settled), svelte/reactivity utilities (SvelteMap, SvelteSet, SvelteDate, MediaQuery), svelte/motion (Spring, Tween), shared state, flushSync, fork, mount/unmount/hydrate, and component testing patterns.'
Svelte 5 State, Context & Utilities
Context API
createContext — Preferred
import { createContext } from 'svelte';
const [getUser, setUser] = createContext<User>();
// Parent: setUser(userData);
// Child: const user = getUser(); // throws if not set
setContext / getContext — Alternative
const KEY = Symbol('my-state');
setContext(KEY, value);
const value = getContext<MyType>(KEY);
hasContext(key)andgetAllContexts()also available
Reactive Context
const state = $state({ count: 0 });
setContext(KEY, state);
// WRONG: state = newObj (breaks the link)
// RIGHT: state.count += 1 (mutate properties)
Global State Risk
- Module-level
$stateleaks between users during SSR - Use context instead for user-specific state
- SPA mode (no SSR): module-level state IS safe
Lifecycle Hooks
onMount
import { onMount } from 'svelte';
onMount(() => {
// runs after DOM mount, NOT during SSR
return () => {
/* cleanup on unmount */
};
});
- GOTCHA: async
onMountreturns Promise — cleanup won't work. Use$effectfor most cases.
onDestroy
- Only lifecycle hook that runs during SSR
- Use for cleanup that must happen server-side too
tick
import { tick } from 'svelte';
await tick(); // resolves when pending state changes applied to DOM
Avoid beforeUpdate / afterUpdate
Use $effect.pre and $effect instead (more granular).
settled
import { settled } from 'svelte';
await settled(); // waits for state changes, async work they trigger, and DOM updates
Stores and Bridges
- Use
.svelte.tsmodules with runes for most app state - Use
svelte/storewhen interoperating with store-based libraries or a store contract is useful writable,readable,derived,readonly,getcome fromsvelte/store$-prefix auto-subscribes to stores in component scripts (must be top-level)
Bridge Utilities
import { fromStore, toStore } from 'svelte/store';
const reactive = fromStore(myStore); // { current } reactive object
const store = toStore(
() => val,
(v) => (val = v)
); // store interface
svelte/reactivity Utilities
Reactive Collections
import { SvelteMap, SvelteSet, SvelteDate, SvelteURL, SvelteURLSearchParams } from 'svelte/reactivity';
- Values inside SvelteMap/SvelteSet are NOT deeply reactive
MediaQuery
import { MediaQuery } from 'svelte/reactivity';
const large = new MediaQuery('min-width: 800px', false); // fallback for SSR
// large.current → boolean
createSubscriber
Bridges external event systems with Svelte reactivity. Only runs when effects are active.
svelte/reactivity/window
import {
innerWidth,
innerHeight,
outerWidth,
outerHeight,
scrollX,
scrollY,
screenLeft,
screenTop,
online,
devicePixelRatio
} from 'svelte/reactivity/window';
// innerWidth.current, scrollY.current, etc.
- All
undefinedon server - GOTCHA:
devicePixelRatiobehavior differs between browsers
svelte/motion
Spring
import { Spring } from 'svelte/motion';
const coords = new Spring({ x: 0, y: 0 }, { stiffness: 0.1, damping: 0.5 });
// coords.current, coords.target
// Spring.of(() => value) for reactive binding
// coords.set(newVal, { instant: true, preserveMomentum: true })
Tween
import { Tween } from 'svelte/motion';
const progress = new Tween(0, { duration: 400, easing: cubicOut });
// progress.current, progress.target
// Tween.of(() => value) for reactive binding
prefersReducedMotion
import { prefersReducedMotion } from 'svelte/motion';
// prefersReducedMotion.current → boolean
Async and Speculative Work
fork
import { fork } from 'svelte';
const pending = fork(() => {
open = true; // evaluated, but DOM changes are held until commit()
});
pending.commit(); // or pending.discard()
- Use for speculative async work before likely user actions
- Discard unused forks to avoid leaking work
Testing Patterns
Setup
- vitest with
resolve: { conditions: ['browser'] }in vite config - Use
.svelte.test.tsfilename to enable runes in tests
Effect Testing
import { flushSync } from 'svelte';
const cleanup = $effect.root(() => {
/* set up effects */
});
flushSync(); // synchronously apply updates
// assert...
cleanup();
Component Testing
import { mount, flushSync } from 'svelte';
const component = mount(MyComponent, { target: document.body, props: { ... } });
flushSync();
expect(document.body.innerHTML).toContain('expected');
Key API Functions
| Function | Purpose |
|---|---|
flushSync(fn?) |
Synchronously flush pending updates |
settled() |
Wait for state changes, triggered async work, and DOM updates |
fork(fn) |
Speculatively evaluate state changes before commit/discard |
untrack(fn) |
Read state without creating dependency |
getAbortSignal() |
AbortSignal that aborts when effect/derived re-runs or destroys |
mount(Comp, opts) |
Create and mount component; call flushSync() in tests |
unmount(comp, { outro }) |
Promise-based removal, optionally waiting for outros |
hydrate(Comp, opts) |
Reuse SSR HTML; call flushSync() in tests if needed |
@references references/context-patterns.md @references references/reactivity-utilities.md @references references/lifecycle-testing.md