Lesson 6 of 9 intermediate 5 min read

Context API

Prop-drilling elimination with setContext(), getContext(), and type-safe symbol keys.

Interactive Laboratory

Live Runtime

Experiment directly with Context API concepts in real time

Keyed List & Control Flow
#1
$state() Signal Root
Keyed node preserved
Rune
#2
$derived() Memoizer
Keyed node preserved
Graph
#3
$effect() DOM Sync
Keyed node preserved
Scheduler

Context API and Dependency Injection

When deeply nested components need access to shared state without passing props through intermediate layers (prop drilling), the Context API provides a hierarchical dependency injection system.


1. Setting and Getting Context

Context is scoped to a component and all of its descendants:

// src/lib/context/theme.ts
import { getContext, setContext } from 'svelte';
 
const THEME_KEY = Symbol('THEME_KEY');
 
export interface ThemeContext {
	isDark: boolean;
	toggle: () => void;
}
 
export function setThemeContext(ctx: ThemeContext) {
	setContext(THEME_KEY, ctx);
}
 
export function getThemeContext(): ThemeContext {
	const ctx = getContext<ThemeContext>(THEME_KEY);
	if (!ctx) throw new Error('getThemeContext must be called within a ThemeProvider');
	return ctx;
}

2. Providing Context in a Root Component

<!-- ThemeProvider.svelte -->
<script lang="ts">
	import type { Snippet } from 'svelte';
	import { setThemeContext } from '#lib/context/theme';
 
	let { children }: { children: Snippet } = $props();
 
	let isDark = $state(true);
 
	setThemeContext({
		get isDark() {
			return isDark;
		},
		toggle: () => {
			isDark = !isDark;
		}
	});
</script>
 
<div class={isDark ? 'dark' : 'light'}>
	{@render children()}
</div>

3. Consuming Context in a Child Component

<!-- DeepChild.svelte -->
<script lang="ts">
	import { getThemeContext } from '#lib/context/theme';
 
	const theme = getThemeContext();
</script>
 
<button onclick={theme.toggle}>
	Current Theme: {theme.isDark ? '🌙 Dark' : '☀️ Light'}
</button>