Skip to main content

Context

Context allows components to access values owned by parent components without passing them down as props (potentially through many layers of intermediate components, known as ‘prop-drilling’).

By creating a [get, set, has] triplet of functions with createContext, you can set the context in a parent component and get it in a child component:

<script>
	import Parent from './Parent.svelte';
	import Child from './Child.svelte';
</script>

<Parent>
	<Child />
</Parent>
<script lang="ts">
	import Parent from './Parent.svelte';
	import Child from './Child.svelte';
</script>

<Parent>
	<Child />
</Parent>
<script>
	import { setUserContext } from './context';

	let { children } = $props();

	setUserContext({ name: 'world' });
</script>

{@render children()}
<script lang="ts">
	import { setUserContext } from './context';

	let { children } = $props();

	setUserContext({ name: 'world' });
</script>

{@render children()}
<script>
	import { getUserContext } from './context';

	const user = getUserContext();
</script>

<h1>hello {user.name}, inside Child.svelte</h1>
<script lang="ts">
	import { getUserContext } from './context';

	const user = getUserContext();
</script>

<h1>hello {user.name}, inside Child.svelte</h1>
import { createContext } from 'svelte';

interface User {
	name: string;
}

export const [getUserContext, setUserContext] = createContext<User>();

createContext was added in version 5.40. If you are using an earlier version of Svelte, you must use setContext and getContext instead.

This is particularly useful when Parent.svelte is not directly aware of Child.svelte, but instead renders it as part of a children snippet as shown above.

setContext and getContext

As an alternative to createContext, you can use setContext and getContext directly. The parent component sets context with setContext(key, value)...

Parent
<script>
	import { setContext } from 'svelte';

	setContext('my-context', 'hello from Parent.svelte');
</script>
<script lang="ts">
	import { setContext } from 'svelte';

	setContext('my-context', 'hello from Parent.svelte');
</script>

...and the child retrieves it with getContext:

Child
<script>
	import { getContext } from 'svelte';

	const message = getContext('my-context');
</script>

<h1>{message}, inside Child.svelte</h1>
<script lang="ts">
	import { getContext } from 'svelte';

	const message = getContext('my-context');
</script>

<h1>{message}, inside Child.svelte</h1>

The key ('my-context', in the example above) and the context itself can be any JavaScript value.

createContext is preferred since it provides better type safety and makes it unnecessary to use keys.

In addition to setContext and getContext, Svelte exposes hasContext and getAllContexts functions.

Using context with state

You can store reactive state in context...

<script>
	import { setCounter } from './context.ts';
	import Child from './Child.svelte';

	let counter = $state({
		count: 0
	});

	setCounter(counter);
</script>

<button onclick={() => counter.count += 1}>
	increment
</button>

<Child />
<Child />
<Child />

<button onclick={() => counter.count = 0}>
	reset
</button>
<script lang="ts">
	import { setCounter } from './context.ts';
	import Child from './Child.svelte';

	let counter = $state({
		count: 0
	});

	setCounter(counter);
</script>

<button onclick={() => counter.count += 1}>
	increment
</button>

<Child />
<Child />
<Child />

<button onclick={() => counter.count = 0}>
	reset
</button>
<script>
	import { getCounter } from './context.ts';

	const counter = getCounter();
</script>

<p>{counter.count}</p>
<script lang="ts">
	import { getCounter } from './context.ts';

	const counter = getCounter();
</script>

<p>{counter.count}</p>
import { createContext } from 'svelte';

interface Counter {
	count: number;
}

export const [getCounter, setCounter] = createContext<Counter>();

...though note that if you reassign counter instead of updating it, you will ‘break the link’ — in other words instead of this...

<button onclick={() => counter = { count: 0 } }>
	reset
</button>

...you must do this:

<button onclick={() => counter.count = 0}>
	reset
</button>

Svelte will warn you if you get it wrong.

Similarly, to pass primitive values through context, use functions as described in Passing state into functions.

Mounting components with context

To mount a component with specific context, create a wrapper component that sets the context before rendering the component. This is useful for component tests, or any other scenario that needs to provide context through mount. As of version 5.49, you can do this sort of thing:

import { mount, unmount } from 'svelte';
import { expect, test } from 'vitest';
import { setUserContext } from './context';
import MyComponent from './MyComponent.svelte';

test('MyComponent', () => {
	function Wrapper(...args) {
		setUserContext({ name: 'Bob' });
		return MyComponent(...args);
	}

	const component = mount(Wrapper, {
		target: document.body
	});

	expect(document.body.innerHTML).toBe('<h1>Hello Bob!</h1>');

	unmount(component);
});

This approach also works with hydrate and render.

The context set by the wrapper only applies to that mounted component tree. Each call to mount, hydrate or render creates a separate wrapper instance, so the context does not leak into other mounted components.

Replacing global state

When you have state shared by many different components, you might be tempted to put it in its own module and just import it wherever it’s needed:

state.svelte
export const myGlobalState = $state({
	user: {
		// ...	}
	// ...});

In many cases this is perfectly fine, but there is a risk: if you mutate the state during server-side rendering (which is discouraged, but entirely possible!)...

App
<script>
	import { myGlobalState } from './state.svelte.js';

	let { data } = $props();

	if (data.user) {
		myGlobalState.user = data.user;
	}
</script>
<script lang="ts">
	import { myGlobalState } from './state.svelte.js';

	let { data } = $props();

	if (data.user) {
		myGlobalState.user = data.user;
	}
</script>

...then the data may be accessible by the next user. Context solves this problem because it is not shared between requests.

Edit this page on GitHub llms.txt

previous next