Context API
In this module, we will learn how to pass data to deeply nested components using the Context API.Course Version History
Nov. 21, 2022 - Updated to SvelteKit v1.0.0-next.549. Changed
index.svelte
to+page.svelte
.
In this module, we will learn about the context API. The context API provides a mechanism for components to communicate data without passing it through the component tree as props. Let's start by reviewing when we would use the context API, and then we will go over how to use it.
Imagine we have an application built with components like this.
You can see we have our root component, homepage
, and within that component are three child components: header
, article
, and footer
. The header component has its own child components, logo
, title
, and nav
. And nav also uses a child component, link
. This example visualizes nested components as they may appear in a project.
Let's assume that we want one of the link components to display the value of pageTitle
which is declared in the root component, homePage
. Using the knowledge we have from previous videos, we can achieve this by passing pageTitle
as a prop to each component. For example, to get pageTitle
to the link component, we will have to first pass it to the header, then to the nav component, and finally into the link component. Even though header and nav components do not need pageTitle
, we have to send it to them in order for them to pass it to components further down in the tree. If you've worked with other component libraries before, you may have heard of this as Prop drilling or Middleman components. Now, in this example that may not seem so bad, but imagine if this were nested 10 levels deep, or even more, as it may be in a real project. That would become a headache to implement, and will make debugging very confusing in the future.
Wouldn't it be nice if we could make pageTitle
directly available to the component without having to forward it through the component tree as props? This is where the context API comes in handy. The context API provides us with a way to pass data through the component tree without having to pass it manually as props. The context API offers two methods, setContext
and getContext
, which can access data associated with a key on the context. The context is just an arbitrary object with some set of keys and values, so anything that can be stored in an object can go in the context. A component can call setContext
, passing in the key and some data as params, and this context becomes available to any of its child components through the use of getContext
.
Let's learn how to use the context API in a real life example.
Here in our index page, we see we are importing a new component, CollectionCard
that displays a highlighted collection, in this case newItems
, which is an array of products that we are then passing into the CollectionCard
component as a prop.
<script>
import GridTile from '$lib/GridTile.svelte';
import CollectionCard from '$lib/CollectionCard.svelte';
let products = [
{
title: 'Cup',
cost: '$10',
src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Cup-front-black.png?v=1623159405',
},
{
title: 'Shirt',
cost: '$10',
src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/short-sleeve-t-shirt-0.png?v=1622902418',
},
];
let newItems = [
{
name: 'Graphic T',
src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Front-NoModel_ec3be051-d579-4c03-b55b-64449d0b0445.png?v=1623255893',
price: '$60.00',
},
{
name: 'Jacket',
src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/bomber-jacket-0.png?v=1622902777',
price: '$80.00',
},
];
</script>
<main>
<CollectionCard collection="{newItems}" />
{#each products as product}
<GridTile {product} />
{/each}
</main>
If we migrate into the CollectionCard
component, we see we are importing another component, ItemsGrid
, which we are again passing our items into as a prop.
<script>
import ItemsGrid from '$lib/ItemsGrid.svelte';
export let collection;
</script>
<ItemsGrid items="{collection}" />
Finally, we are actually using the value of our items
prop in this ItemsGrid
component.
<script>
export let items;
</script>
<div class="flex items-center">
{#each items as item}
<div
class="relative m-2 flex h-40 w-1/2 items-center justify-center overflow-hidden bg-white/20"
>
<img src="{item.src}" class="h-full" alt="" />
<div class="absolute bottom-0 right-0 bg-black p-2">{item.name}</div>
</div>
{/each}
</div>
Now let’s use the context API to set the value of items in our root component, and get that data in our ItemsGrid
component, without forwarding it through the component tree.
Now in order to get the value of newItems
from our root component into this component, we first need to provide the context value in our root component. Next move back to +page.svelte
and import the setContext
function that is provided to us by Svelte.
Now in our script we can invoke setContext
which accepts two arguments, the key
and value
. The context object can be anything, so let’s set our newItems context like this,
import { setContext } from 'svelte';
setContext('newItemsContext', newItems);
Where the string newItemsContext
is our key and we are setting its value to that of newItems
. Now the context is set, so we can go back to our ItemsGrid
component and get the newItemsContext
value. To do this, we will import the getContext
method from Svelte. We can then use this method to get the value of our newItemsContext
. To do this we write:
import { getContext } from 'svelte';
let items = getContext('newItemsContext');
Now, let’s go through each component and remove their props. If we check out our browser, we can see the value of newItems
is successfully being displayed. To double check, we can change the value of newItems
in our root component. Let’s move into +page.svelte
and change the title from ‘T-Shirt’ to ‘Graphic T’. Looking back in our browser we see that our component updated with the new value.
We now have an efficient way to pass data down the component tree, even a deeply nested one! It is important to remember that using the context API only passes data downstream, so from parent to child. Now, eventually, most apps will have values that need to be accessed by multiple unrelated components. In the next module we will learn how to share state between all components using stores.
Was this helpful?