Context API
Svelte context: setContext / getContext for sharing values down the tree without prop drilling.
Svelte — context
EXAMPLE
<!-- ===== When to use context ===== -->
<!--
- Theme / locale / current user
- DI for stores or service instances
- Plugin-style relationships (parent provides; descendants consume)
Avoid for general state — stores or query libraries are simpler.
-->
<!-- ===== Parent provides ===== -->
<!-- App.svelte -->
<script>
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
import Header from './Header.svelte';
const theme = writable('light');
setContext('theme', theme);
</script>
<Header />
<!-- ===== Child consumes ===== -->
<!-- Header.svelte -->
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<header class={$theme}>
<button on:click={() => $theme = $theme === 'light' ? 'dark' : 'light'}>
Toggle
</button>
</header>
<!-- ===== Symbol keys (recommended) ===== -->
<!-- shared.js -->
export const THEME = Symbol('theme');
<!-- App.svelte -->
import { THEME } from './shared.js';
setContext(THEME, theme);
<!-- Header.svelte -->
import { THEME } from './shared.js';
const theme = getContext(THEME);
<!-- Avoids string-key collisions across packages. -->
<!-- ===== Context flows DOWN, not across ===== -->
<!--
setContext must be called in the SAME COMPONENT's <script>, NOT in onMount.
Any descendant rendered AFTER the setContext call can read it.
Sibling components in different trees do NOT share context.
-->
<!-- ===== Typed context (TS) ===== -->
<!-- shared.ts -->
import type { Writable } from 'svelte/store';
export const THEME = Symbol('theme') as unknown as { __type: Writable<string> };
export function getTheme() { return getContext(THEME) as Writable<string>; }
<!-- ===== Service / DI pattern ===== -->
<!-- Provide a service object: -->
import { setContext } from 'svelte';
const api = {
list: () => fetch('/api/items').then(r => r.json()),
};
setContext('api', api);
<!-- Consumers: -->
const api = getContext('api');
let items = [];
onMount(async () => items = await api.list());
<!-- ===== Forms + nested components ===== -->
<!-- A common pattern: a Form component provides validation context
and Input children read it. -->
<script>
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
const errors = writable({});
setContext('form', { errors });
</script>
<!-- Input.svelte -->
<script>
import { getContext } from 'svelte';
export let name;
const { errors } = getContext('form');
</script>
<input bind:value />
{#if $errors[name]}<span>{$errors[name]}</span>{/if}
<!-- ===== Patterns to internalise =====
- Use SYMBOL keys for context
- Provide stores (not raw values) so consumers can react
- Use for cross-cutting concerns (theme, auth, plugins)
- Type your context with TS where possible
-->
<!-- ===== Pitfalls =====
- Calling setContext in onMount -> too late; descendants read undefined
- String key collisions across packages -> use Symbols
- Forgetting that context does not work across portals / detached trees
- Treating context as a global event bus
-->
Why it matters
Context is the DI mechanism in Svelte. setContext in the provider, getContext in descendants, Symbol keys to avoid collisions. Pair it with stores for reactivity. Reach for it on cross-cutting concerns (theme, auth, plugins) and not as a general state container.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { setContext, getContext } from 'svelte';
setContext('theme', 'dark');
const theme = getContext('theme');
Try it Yourself »
Discussion
Loading…