articles

Combining the Context API with Stores in Svelte

Published on July 10, 2020

See this Repl for a working example of this code


Svelte’s Context API is a bit of a misunderstood feature that a lot of users struggle with. The core concept of it however is very simple: it allows you to create a key-value store that is only accessible to the current component and all it’s descendants. This is a very nice feature to have if you work with component composition and the use of slots as I will discuss further down.

How to use the Context API

This feature comes with two functions: setContext and getContext, which as their names suggests, set something and get something (preferable the same thing), they are part of the svelte package and can be imported from there:

<script>
    import { setContext } from 'svelte'

    setContext('key', value)  // Add the key/value pair
</script>

And in one of the components that are a descendant of this component

<script>
    import { getContext } from 'svelte'

    const value = getContext('key')  // Gets the value stored as key
</script>
setContext and getContext have to be called during component initialization. Think of this as being in the top level of your component.

Why use the Context API ?

When dealing with children we can always just pass the values as properties, but this would mean that if a variable has to be use deeper into the nesting tree it’s has to be passed around through a series of components adding extra overhead to each of those intermediate components. Since the context is available to all descendants using it here will eliminate the need of passing the property around.

Another place this can be a real saviour is when dealing with component composition and slots where the children are completely unknown to the current component. They key/value pairs of the Context API are however fully available from within the slotted content as they are a descendant of the component.

Values in the store are not reactive! Once the value is set it does not change anymore

Making the Context Reactive

Having the value not change anymore is however not what you always want, luckily enough the solution for this as simple as using Svelte: pass a store to the context

<script>
    import { setContext } from 'svelte'
    import { writable } from 'svelte/store'

    const value = writable(123)
    setContext('key', value)
</script>

and somewhere else

<script>
    import { getContext } from 'svelte'

    const value = getContext('key')
<script>

<span>value: {$value}</span>

Notice here that since it’s a store you have to use the $ notation to get the value.

Tracking the initial value

We can use this to keep track of the initial values of a component and provide the user with a Reset opportunity. Take the following code

<script>
	import { getContext, setContext } from 'svelte'
	import { writable } from 'svelte/store'
	
	import Child from './Child.svelte'
	
	let name = 'world';
	let nameStore = writable(name)
	
	$: nameStore.set(name)
	
	setContext('initial', name)
	setContext('name', nameStore)
	
	const reset = () => name = getContext('initial')
	
</script>

<input bind:value={name}>

<Child />

<button on:click={reset}>Reset</button>
<script>
	import { getContext } from 'svelte'
	
	const name = getContext('name')
	const initial = getContext('initial')
</script>

<div>
	<p>Initial value: {initial}</p>
	<p>Current value: {$name}</p>
</div>

In this little program the user will be shown an inputfield, reset button and two lines of text: one with the initial value and one with what is currently entered in the input box. When we did setContext('initial', name) we entered the current value of name into the context and as said before: context values are not reactive which means this value will never change anymore, this makes it possible to recall this value and make the reset work.