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.
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.
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));
}
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.