Skip to content

Latest commit

 

History

History
138 lines (100 loc) · 3.93 KB

02-context.md

File metadata and controls

138 lines (100 loc) · 3.93 KB
title
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'). The parent component sets context with setContext(key, value)...

<!--- file: Parent.svelte --->
<script>
	import { setContext } from 'svelte';

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

...and the child retrieves it with getContext:

<!--- file: Child.svelte --->
<script>
	import { getContext } from 'svelte';

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

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

This is particularly useful when Parent.svelte is not directly aware of Child.svelte, but instead renders it as part of a children snippet (demo):

<Parent>
	<Child />
</Parent>

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

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

Using context with state

You can store reactive state in context (demo)...

<script>
	import { setContext } from 'svelte';
	import Child from './Child.svelte';

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

	setContext('counter', counter);
</script>

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

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

...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.

Type-safe context

A useful pattern is to wrap the calls to setContext and getContext inside helper functions that let you preserve type safety:

/// file: context.js
// @filename: ambient.d.ts
interface User {}

// @filename: index.js
// ---cut---
import { getContext, setContext } from 'svelte';

const key = {};

/** @param {User} user */
export function setUserContext(user) {
	setContext(key, user);
}

export function getUserContext() {
	return /** @type {User} */ (getContext(key));
}

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:

/// file: state.svelte.js
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!)...

<!--- file: App.svelte ---->
<script>
	import { myGlobalState } from 'svelte';

	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.