svelte-state-context

star 1

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.

dhinesh03 By dhinesh03 schedule Updated 6/6/2026

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) and getAllContexts() 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 $state leaks 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 onMount returns Promise — cleanup won't work. Use $effect for 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.ts modules with runes for most app state
  • Use svelte/store when interoperating with store-based libraries or a store contract is useful
  • writable, readable, derived, readonly, get come from svelte/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 undefined on server
  • GOTCHA: devicePixelRatio behavior 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.ts filename 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

Install via CLI
npx skills add https://github.com/dhinesh03/svelte-kit-spa-starter --skill svelte-state-context
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator