Context API

In this module, we will learn how to pass data to deeply nested components using the Context API.
Table of Contents
Context APIView the code for this module.
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.

+page.svelte
<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.

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

ItemsGrid.svelte
<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,

+page.svelte
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:

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