cf.completefrontend
Open lab My progress
THE REACT FIELD GUIDE

Passing data deeply with context

Stop passing props through every level: create a context, provide it from a parent, read it with useContext or use, and learn when props are still better.

By the end, you can
  • 01
    Replace prop drilling with contextCreate a context, provide a value with React 19's <ThemeContext value>, and read it with useContext or use.
  • 02
    Predict what a component readsFind the nearest provider above a component, override a value for part of the tree, and know when the default is used.
  • 03
    Know the cost and the alternativesExplain why every reader renders again when the value changes, and choose props or children when context isn't needed.

A theme that every level has to pass along

A shop lets you pick light or dark buttons. The choice lives in App, at the top, but the buttons sit four components down. With props, the only way to get it there is to hand it from each component to the next. Tick Dark buttons in the result:

A theme passed down through every componentPop out in the code editor (opens in a new tab)JSX
import { useState } from "react"; export default function App() {  const [theme, setTheme] = useState("light");   return (    <>      <label>        <input type="checkbox" checked={theme === "dark"}          onChange={(e) => setTheme(e.target.checked ? "dark" : "light")} />        Dark buttons      </label>      <ShopPage theme={theme} />    </>  );} function ShopPage({ theme }) {  return (    <main>      <h3>Tea shop</h3>      <ProductList theme={theme} />    </main>  );} function ProductList({ theme }) {  return (    <ul>      <ProductCard name="Green tea" theme={theme} />      <ProductCard name="Chai" theme={theme} />    </ul>  );} function ProductCard({ name, theme }) {  return (    <li>      {name} <AddButton theme={theme} />    </li>  );} function AddButton({ theme }) {  return <button className={theme}>Add to cart</button>;}
ResultLoading…

Line 4 keeps theme in App's state, and lines 9 and 10 switch it with the checkbox. Line 13 passes it to ShopPage, which passes it to ProductList on line 22, which passes it to each ProductCard on lines 30 and 31, which passes it to AddButton on line 39. Only line 45 uses it, to pick the button's className; the styles are in the styles.css tab.

It works, but three of the five components take a theme prop they never use, only to pass it on. This is called prop drilling. React has a way to skip the middle: context. A component high in the tree provides a value, and any component below it, however deep, reads it directly.

Two columns of the same five components: App, ShopPage, ProductList, ProductCard, AddButton. Left, with props: every arrow down carries theme. Right, with context: the arrows down carry nothing, and one curved arrow goes from App straight to AddButton.With propsAppShopPageProductListProductCardAddButtonthemethemethemethemeWith contextAppShopPageProductListProductCardAddButtontheme
With props, theme travels through ShopPage, ProductList, and ProductCard, which only pass it on. With context, App provides it once, and AddButton reads it directly.
Real-life analogyWi-Fi in a house

To get the internet to a laptop in the back bedroom, you could run a cable from the router through the hall, the kitchen, and the living room. Or the router can send the signal through the air, and the laptop joins it from wherever it is. The rooms in between don't carry anything.

In real life: Running a cable through every room to reach one laptop
In React: Passing theme through every component to reach AddButton
In real life: A router that sends the signal through the house
In React: A provider: <ThemeContext value={theme}>
In real life: Any device in range connects, in any room
In React: Any component below the provider calls useContext(ThemeContext)
In real life: A device joins the nearest router
In React: A component reads the nearest provider above it
In real life: Outside the house, a device falls back to its own data plan
In React: With no provider above, a component gets the default value
The main idea

Context lets a component provide a value to every component below it in the tree, without passing props through each level. You create a context with createContext, provide a value with <ThemeContext value={…}>, and read it with useContext(ThemeContext) or use(ThemeContext).

The problem with passing props through every level

Passing props is React's normal way to move data down, and most of the time it's the right way: you can read any component and see exactly what it gets. Drilling becomes a problem when a value has to go deep, or to many places, through components that don't care about it. In the shop above:

  • ShopPage, ProductList, and ProductCard each list theme in their props (lines 18, 27, and 36), and each hands it on. If the header later needs the theme too, every component on the way to it needs a new prop.
  • Renaming theme means editing all five components, even though only one of them uses it.
  • Moving AddButton somewhere else in the tree means building a new path of props to its new place.

Drilling often grows out of lifting state up: once state moves to a common parent near the top, every component that needs it is far away. How to read the table: each row is one change to the shop, and what it costs with props and with context.

Props or context, for a value many levels down
When you…With props passed through every levelWith context
Add a new component deep down that needs the themeAdd a theme prop to every component on the way to it.Call useContext(ThemeContext) in the new component.
Rename themeEdit every component that passes it, even ones that never use it.Edit the provider and the components that read it.
Move AddButton to another part of the pageBuild a new path of props to its new place.Nothing, as long as it's still below the provider.
Read a component to see where its data comes fromIts props say so: follow them up to the parent.Find the nearest provider above it, which may be far away.

The next three steps replace the drilled prop with context: create it, read it, and provide it.

Step 1: create a context

A context is made once, outside any component, with createContext. Most apps put it in its own file, so the component that provides it and the components that read it can all import it. This file is the ThemeContext.js tab in every example below; it only runs as part of them:

ThemeContext.js: create the contextJavaScript
import { createContext } from "react";

export const ThemeContext = createContext("light");

Line 1 imports createContext from React. Line 3 calls it and exports what it returns as ThemeContext. Two things to know about that line:

  • The context object, ThemeContext, doesn't hold the current theme. It's a name both sides agree on: a provider says "here is the value for ThemeContext", and a reader asks "what is ThemeContext here?".
  • The argument, "light", is the default value. A component gets it only when there is no provider for ThemeContext anywhere above it. It never changes. Use null when there's no sensible default, like the signed-in user.

Step 2: read it with useContext or use

A component reads a context by passing the context object to useContext. Here a button reads the theme, and nothing provides one yet:

A button that reads the theme, with no providerPop out in the code editor (opens in a new tab)JSX
import { useContext } from "react";import { ThemeContext } from "./ThemeContext.js"; export default function App() {  return (    <ul>      <li>        Green tea <AddButton />      </li>    </ul>  );} function AddButton() {  const theme = useContext(ThemeContext);  return <button className={theme}>Add to cart ({theme})</button>;}
ResultLoading…

Line 2 imports ThemeContext from its file. Line 15 calls useContext(ThemeContext) and names the result theme. Line 16 uses it for the class and the label. The button says Add to cart (light): React looked up the tree for a ThemeContext provider, found none, and returned the default from createContext("light"). There is no error and no warning, so a forgotten provider can go unnoticed; Exercise 2 has one.

useContext is a hook, so it follows the rules from State: call it at the top level of the component, not inside an if or after an early return. React 19 added a second way to read a context, use, which doesn't have that limit. Here, a sold-out product returns early, and only an available one reads the theme:

use reads the theme after an early returnPop out in the code editor (opens in a new tab)JSX
import { use } from "react";import { ThemeContext } from "./ThemeContext.js"; export default function App() {  return (    <ThemeContext value="dark">      <ul>        <li>Green tea <AddButton soldOut={false} /></li>        <li>Chai <AddButton soldOut={true} /></li>      </ul>    </ThemeContext>  );} function AddButton({ soldOut }) {  if (soldOut) {    return <span>Sold out</span>;  }  const theme = use(ThemeContext);  return <button className={theme}>Add to cart ({theme})</button>;}
ResultLoading…

Lines 16 to 18 return Sold out for Chai. Line 19 runs only for Green tea, and calls use(ThemeContext) after that early return, which useContext isn't allowed to do. Line 6 provides "dark", so Green tea's button says Add to cart (dark). Step 3 explains that line.

How to read the table: each row is one question, with the answer for each way of reading a context.

useContext or use
QuestionuseContext(ThemeContext)use(ThemeContext)
Where can you call it?Only at the top level of a component or hook.Anywhere in a component or hook, even inside an if or after an early return, but not inside try and catch.
What does it return for a context?The nearest provider's value, or the default.The same: the nearest provider's value, or the default.
Which React versions have it?Every version with hooks, so most code you read uses it.React 19 and later.
What else can it read?Only a context.A Promise too, which The use API lesson covers.

For a context, both return the same value: the nearest provider's, or the default. use can also read a Promise, which The use API lesson covers. This lesson uses useContext where a component reads at the top level, as most code you'll read does, and use where the read comes after a condition.

Step 3: provide a value from a parent

To give the components below a value, render the context object itself as a component, with a value prop, around them. That's a provider. Here is the overview's shop with context. Tick Dark buttons: both buttons change, and no component in the middle mentions the theme.

The same shop with contextPop out in the code editor (opens in a new tab)JSX
import { useContext, useState } from "react";import { ThemeContext } from "./ThemeContext.js"; export default function App() {  const [theme, setTheme] = useState("light");   return (    <ThemeContext value={theme}>      <label>        <input type="checkbox" checked={theme === "dark"}          onChange={(e) => setTheme(e.target.checked ? "dark" : "light")} />        Dark buttons      </label>      <ShopPage />    </ThemeContext>  );} function ShopPage() {  return (    <main>      <h3>Tea shop</h3>      <ProductList />    </main>  );} function ProductList() {  return (    <ul>      <ProductCard name="Green tea" />      <ProductCard name="Chai" />    </ul>  );} function ProductCard({ name }) {  return (    <li>      {name} <AddButton />    </li>  );} function AddButton() {  const theme = useContext(ThemeContext);  return <button className={theme}>Add to cart</button>;}
ResultLoading…

Line 8 opens <ThemeContext value={theme}> and line 15 closes it. Everything between them, and everything those components render, however deep, can read theme. ShopPage, ProductList, and ProductCard (lines 19 to 43) no longer take or pass a theme prop. AddButton reads it on line 46.

When you tick the box, setTheme gives App a new theme, and line 8 provides the new value. React renders again every component that reads ThemeContext, so both buttons turn dark. A context isn't only for values that never change: put state in the value, and the readers follow it.

Providers in code written before React 19

Rendering <ThemeContext> directly as a provider is new in React 19. Before that, and in much of the code you'll read today, a provider is written with .Provider:

The same provider in older codeJSX
<ThemeContext.Provider value={theme}>
  <ShopPage />
</ThemeContext.Provider>

It means exactly the same thing, and still works in React 19. The React team has said it will deprecate .Provider in a future version, so write new providers as <ThemeContext value={…}>.

The nearest provider wins

A provider can sit inside another provider of the same context. A component reads the nearest one above it. That lets you override a value for one part of the page. This page is light, but its promo box is always dark. Press Switch page theme a few times:

A dark promo box inside a page with its own themePop out in the code editor (opens in a new tab)JSX
import { useContext, useState } from "react";import { ThemeContext } from "./ThemeContext.js"; export default function App() {  const [theme, setTheme] = useState("light");   return (    <ThemeContext value={theme}>      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>        Switch page theme      </button>      <p>        Green tea <AddButton />      </p>      <section className="promo">        <ThemeContext value="dark">          <p>            Today only: Chai <AddButton />          </p>        </ThemeContext>      </section>    </ThemeContext>  );} function AddButton() {  const theme = useContext(ThemeContext);  return <button className={theme}>Add to cart ({theme})</button>;}
ResultLoading…

Line 8 provides the page's theme to everything inside it. Lines 16 to 20 put a second provider, with the value "dark", around the promo. The Green tea button on line 13 has one provider above it, and follows the switch: light, then dark. The Chai button on line 18 has two, and reads the nearer one on line 16, so it says dark whatever the page theme is.

A page window with a ThemeContext provider whose value is light. Inside it, a Green tea button, with an arrow up to the light provider. Also inside it, a promo window with its own provider whose value is dark, holding a Chai button with an arrow up to the dark provider.page<ThemeContext value="light">Green tea: lightpromo<ThemeContext value="dark">Chai: dark
Each AddButton reads the closest ThemeContext above it. The Green tea button finds the page's light; the Chai button finds the promo's dark first, so the page's value never reaches it.

Try it with the providers switched on and off. The playground renders both buttons with real context. Choose a value for each provider, or none to take it out, and read what each button gets and from where.

Playground: which provider does each button read?
The page, with the providers you choseJSX
const ThemeContext = createContext("light");

function Page() {
  return (
    <ThemeContext value="light">
      <header>
        Green tea <AddButton />
      </header>
      <section>
        <ThemeContext value="dark">
          Chai <AddButton />
        </ThemeContext>
      </section>
    </ThemeContext>
  );
}
What each button readsuseContext(ThemeContext)
page provider: "light"Green tea Add to cart (light)from the page provider
promo provider: "dark"Chai Add to cart (dark)from the promo provider
Try it yourself
Page provider
Promo provider

The Green tea button reads light, from the page provider. The Chai button has two providers above it, and the nearest one, the promo provider, wins: dark.

Both buttons run here with real React context, made with createContext("light"). The code shows the providers you chose; none means that provider is left out. Reset puts back a light page with a dark promo.

Take the promo provider out, and Chai reads the page's value like Green tea. Take both out, and both buttons fall back to the default, "light". Each context is separate, too: a provider of ThemeContext never hides the value of another context, such as a UserContext.

Every reader renders again when the value changes

When a provider's value changes, React renders again every component below it that reads that context. To see it, each component in this shop shows how many times it has rendered. The counter comes from the RenderCount.jsx tab; you don't need to read it to use it. Press Switch theme twice:

Render counters on every componentPop out in the code editor (opens in a new tab)JSX
import { createContext, useContext, useState } from "react";import RenderCount from "./RenderCount.jsx"; const ThemeContext = createContext("light"); function ThemeSwitch({ children }) {  const [theme, setTheme] = useState("light");   return (    <ThemeContext value={theme}>      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>        Switch theme      </button>      {children}    </ThemeContext>  );} function Header() {  return <h3>Tea shop <RenderCount /></h3>;} function ThemeLabel() {  const theme = useContext(ThemeContext);  return <p>Theme: {theme} <RenderCount /></p>;} function ProductList() {  return (    <section>      <p>Products <RenderCount /></p>      <AddButton />    </section>  );} function AddButton() {  const theme = useContext(ThemeContext);  return <button className={theme}>Add to cart <RenderCount /></button>;} export default function App() {  return (    <ThemeSwitch>      <Header />      <ThemeLabel />      <ProductList />    </ThemeSwitch>  );}
ResultLoading…

ThemeSwitch (lines 6 to 17) keeps the theme in state and provides it on line 10. ThemeLabel (line 24) and AddButton (line 38) read it. Header and ProductList don't. After two clicks, Theme: light and Add to cart say renders: 3, while Tea shop and Products still say renders: 1.

ThemeSwitch at the top, its theme changing from light to dark. Below it, Header, ThemeLabel, and ProductList; below ProductList, AddButton. ThemeLabel and AddButton read the context and render again. Header and ProductList don't read it and aren't called again.ThemeSwitchtheme: light → darkHeaderdoesn't read it:not called againThemeLabelreads it:renders againProductListnot called againAddButtonreads it: renders again
When ThemeSwitch's theme changes, React renders again every component that reads ThemeContext: ThemeLabel and AddButton. Header and ProductList don't read it, and React doesn't call them again.

Why do Header and ProductList stay at 1? App made them, on lines 45 to 47, and passed them to ThemeSwitch as children. When the theme changes, only ThemeSwitch renders again; its children are the same as last time, so React doesn't call them. But it does call every component that reads the context, even AddButton, deep inside ProductList, which didn't render.

React decides whether the value changed by comparing the old and new value with Object.is. A string like "dark" compares by its text. An object compares by identity: if a provider gets a new object, like value={{ theme, user }} written in the component, every reader renders again, even one that only uses user and even when nothing in it changed.

For most apps, this costs nothing you would notice: a theme or a signed-in user changes rarely. When it does matter, there are tools for it, like splitting one big context into several, which the next lesson does, and reading the context lower down, which When React re-renders covers. Press Edit & run and add useContext(ThemeContext); as the first line of Header: it now reads the context, and its counter follows the clicks.

Before you reach for context

Context is easy to overuse. A value passed through two or three components is often clearer as props, because each component shows what it gets. Before you add a context, try these two things first:

  1. Pass props. Many props through several components is normal, and the data flow stays easy to follow.
  2. Pass JSX as children. If components in the middle only place the data on the page, let them take children, and give the data to the component that needs it directly.

Here, a list of teas passes through a Layout and a Main that only pass it on:

A list passed through two layout componentsPop out in the code editor (opens in a new tab)JSX
const teas = ["Green tea", "Chai", "Mint tea"]; export default function App() {  return <Layout teas={teas} />;} function Layout({ teas }) {  return (    <div className="layout">      <h3>Tea shop</h3>      <Main teas={teas} />    </div>  );} function Main({ teas }) {  return (    <main>      <TeaList teas={teas} />    </main>  );} function TeaList({ teas }) {  return <p>{teas.length} teas: {teas.join(", ")}</p>;}
ResultLoading…

Line 4 hands teas to Layout, line 11 hands it to Main, and line 19 to TeaList, the only component that uses it. Now the same page with children:

The same page with childrenPop out in the code editor (opens in a new tab)JSX
const teas = ["Green tea", "Chai", "Mint tea"]; export default function App() {  return (    <Layout>      <Main>        <TeaList teas={teas} />      </Main>    </Layout>  );} function Layout({ children }) {  return (    <div className="layout">      <h3>Tea shop</h3>      {children}    </div>  );} function Main({ children }) {  return <main>{children}</main>;} function TeaList({ teas }) {  return <p>{teas.length} teas: {teas.join(", ")}</p>;}
ResultLoading…

Lines 5 to 9 put TeaList inside Main inside Layout, right in App, and give it teas on line 7. Layout and Main show whatever children they get (lines 17 and 23) and know nothing about teas. The result is the same: 3 teas: Green tea, Chai, Mint tea.

Left: App, Layout, Main, and TeaList in a column, with teas on every arrow. Right: App above a Layout window holding a Main window holding TeaList, written in App with teas passed to it directly.Passing it downAppLayoutMainTeaListteasteasteasPassing childrenAppLayoutMain<TeaList teas={teas} />
Left, teas passes through Layout and Main. Right, App puts TeaList inside them as children and gives it teas itself: no context, and no layers in between.

Context is the right tool when many components in different parts of the tree need the same value. Common cases:

  • A theme, or other display settings like a language, that buttons and cards all over the page read.
  • The signed-in user, read by the header, the cart, and a review form.
  • The current page address: routing libraries keep it in a context, so every link can tell whether it's the active one.
  • State managed near the top that far-away components need to read and change. The next lesson combines a reducer with context for this.

A shop with a theme and a signed-in user

Real apps often have a few contexts side by side. This shop has two: the theme, and the signed-in user. Press Sign in, then Switch theme, then Sign out.

Two contexts in one shopPop out in the code editor (opens in a new tab)JSX
import { use, useState } from "react";import { ThemeContext, UserContext } from "./contexts.js"; export default function App() {  const [theme, setTheme] = useState("light");  const [user, setUser] = useState(null);   return (    <ThemeContext value={theme}>      <UserContext value={user}>        <Header          onSwitchTheme={() => setTheme(theme === "light" ? "dark" : "light")}          onSignIn={() => setUser({ name: "Asha" })}          onSignOut={() => setUser(null)}        />        <p>          Green tea <AddButton />        </p>        <ThemeContext value="dark">          <footer>            Need help? <AddButton label="Chat with us" />          </footer>        </ThemeContext>      </UserContext>    </ThemeContext>  );} function Header({ onSwitchTheme, onSignIn, onSignOut }) {  const user = use(UserContext);   return (    <header>      <button onClick={onSwitchTheme}>Switch theme</button>{" "}      {user ? (        <>          Hi, {user.name} <button onClick={onSignOut}>Sign out</button>        </>      ) : (        <button onClick={onSignIn}>Sign in</button>      )}    </header>  );} function AddButton({ label = "Add to cart" }) {  const theme = use(ThemeContext);  const user = use(UserContext);  return (    <button className={theme} disabled={!user}>      {user ? label : "Sign in to " + label.toLowerCase()}    </button>  );}
ResultLoading…

The contexts.js tab creates both: ThemeContext with the default "light", and UserContext with null, for nobody signed in. App keeps both in state (lines 5 and 6) and provides them on lines 9 and 10. The header's buttons call functions App passes as props on lines 12 to 14, because App owns the state; the next lesson shows how to change state through context instead.

  • Header reads the user on line 30 and shows Hi, Asha with a Sign out button, or a Sign in button.
  • AddButton reads both contexts on lines 47 and 48. While nobody is signed in, it's turned off and says Sign in to add to cart.
  • Lines 19 to 23 override the theme for the footer, so its button stays dark while the page switches. The footer's button still reads the user from line 10: overriding one context leaves the other alone.
Context or props?
  • The light or dark theme every button and card reads
  • The signed-in user, read by the header, the cart, and the reviews
  • A product's price, passed from ProductCard to its PriceTag
  • The language the whole page is shown in
  • An onAdd handler that a card passes to its own button
  • The current page address, read by every link to mark the active one
  • The search text, shared by the search box and the list beside it
  • A list that passes through a Layout that only places it on the page
Try it yourself
0 of 8 correct

Sort each piece of data: is it a good fit for context, or should it travel as props or children?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "The context object holds the value." No. ThemeContext is only a name both sides share. The value comes from the nearest provider, or the default.
  • "Forgetting the provider causes an error." No. The reader quietly gets the default value, like the button that said light with no provider.
  • "A component can read the value it provides." No. useContext looks for providers above the component. A provider in the same component's output only reaches its children.
  • "Context replaces props." No. Props are still the normal way to pass data. Context is for values many components far apart need.
  • "Only components that use part of the value render again." No. When the value changes, every component that reads that context renders again.
  • "You need .Provider to provide a context." Not in React 19. Render <ThemeContext value={…}> directly; .Provider is the older form.

Practice exercises

Each exercise shows its code's result under it, or when you press Edit & run, and updates as you type. Guess first, then use the hints.

Exercise 1 · Warm-upPredict the nearest provider

Read the code without running it. What does the Footer line show? Then press Edit & run to check.

import { createContext, useContext } from "react";

const ThemeContext = createContext("light");

export default function App() {
  return (
    <>
      <Label place="Header" />
      <ThemeContext value="dark">
        <Label place="Page" />
        <ThemeContext value="light">
          <Label place="Banner" />
        </ThemeContext>
        <Label place="Footer" />
      </ThemeContext>
    </>
  );
}

function Label({ place }) {
  const theme = useContext(ThemeContext);
  return <p>{place}: {theme}</p>;
}

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFind the missing provider

    App starts with a dark theme, but the button says light, and Switch theme does nothing. Fix it. What does the button say before you click anything?

    import { createContext, useContext, useState } from "react"; const ThemeContext = createContext("light"); export default function App() {  const [theme, setTheme] = useState("dark");   return (    <>      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>        Switch theme      </button>      <Toolbar />    </>  );} function Toolbar() {  return (    <p>      <ThemeButton />    </p>  );} function ThemeButton() {  const theme = useContext(ThemeContext);  return <button className={theme}>Theme: {theme}</button>;}
    ResultLoading…

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeReplace a drilled prop with context

      The user passes through Page and Sidebar to reach Greeting. Give it to Greeting with a UserContext instead. What does the page show when you're done?

      const user = { name: "Asha" }; export default function App() {  return <Page user={user} />;} function Page({ user }) {  return (    <main>      <Sidebar user={user} />    </main>  );} function Sidebar({ user }) {  return (    <aside>      <Greeting user={user} />    </aside>  );} function Greeting({ user }) {  return <p>Hi, {user.name}</p>;}
      ResultLoading…

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeMake the footer dark

        Keep the page light, but make only the footer's button dark, without changing ThemeButton. What does the footer button say?

        import { createContext, useContext } from "react"; const ThemeContext = createContext("light"); export default function App() {  return (    <ThemeContext value="light">      <p>        Green tea <ThemeButton label="Add to cart" />      </p>      <footer>        <ThemeButton label="Contact us" />      </footer>    </ThemeContext>  );} function ThemeButton({ label }) {  const theme = useContext(ThemeContext);  return <button className={theme}>{label} ({theme})</button>;}
        ResultLoading…

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeIndent replies by their depth

          Every comment says depth 0, so the replies don't indent. Make each Comment provide one more than its own depth to its replies. What depth does Add milk, then. show?

          import { createContext, useContext } from "react"; const DepthContext = createContext(0); const thread = {  text: "Is this tea strong?",  replies: [    {      text: "Quite strong.",      replies: [{ text: "Add milk, then.", replies: [] }],    },  ],}; export default function App() {  return <Comment comment={thread} />;} function Comment({ comment }) {  const depth = useContext(DepthContext);   return (    <div style={{ marginLeft: depth * 16 }}>      <p>        {comment.text} (depth {depth})      </p>      {comment.replies.map((reply) => (        <Comment key={reply.text} comment={reply} />      ))}    </div>  );}
          ResultLoading…

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengePass children instead

            This is the drilled layout from above. Without using context, change it so teas goes straight from App to TeaList. After your change, how many components take a teas prop?

            const teas = ["Green tea", "Chai", "Mint tea"]; export default function App() {  return <Layout teas={teas} />;} function Layout({ teas }) {  return (    <div className="layout">      <h3>Tea shop</h3>      <Main teas={teas} />    </div>  );} function Main({ teas }) {  return (    <main>      <TeaList teas={teas} />    </main>  );} function TeaList({ teas }) {  return <p>{teas.length} teas: {teas.join(", ")}</p>;}
            ResultLoading…

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each code question, find the reader, then walk up the tree to the first provider of that context. Press Edit & run on a question's code to check your answer.

              The context quiz · 8 questionsScore: first tries count
              1. Question 1 of 8What does the argument to createContext("light") do?

                Choose an answer to see the explanation.

              2. Question 2 of 8What does Status show?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                import { createContext, useContext } from "react";
                
                const ThemeContext = createContext("light");
                const LanguageContext = createContext("English");
                
                export default function App() {
                  return (
                    <ThemeContext value="dark">
                      <LanguageContext value="Hindi">
                        <ThemeContext value="light">
                          <Status />
                        </ThemeContext>
                      </LanguageContext>
                    </ThemeContext>
                  );
                }
                
                function Status() {
                  const theme = useContext(ThemeContext);
                  const language = useContext(LanguageContext);
                  return <p>{theme}, {language}</p>;
                }

                Choose an answer to see the explanation.

              3. Question 3 of 8Toolbar reads the theme and also provides "dark". What does the paragraph show?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                import { createContext, useContext } from "react";
                
                const ThemeContext = createContext("light");
                
                export default function App() {
                  return <Toolbar />;
                }
                
                function Toolbar() {
                  const theme = useContext(ThemeContext);
                
                  return (
                    <ThemeContext value="dark">
                      <p>Toolbar theme: {theme}</p>
                    </ThemeContext>
                  );
                }

                Choose an answer to see the explanation.

              4. Question 4 of 8In a React 19 app, how should you write a new provider for ThemeContext?

                Choose an answer to see the explanation.

              5. Question 5 of 8A component returns early when a product is sold out, and reads the theme after that return. Which call is allowed there?

                Choose an answer to see the explanation.

              6. Question 6 of 8In the render-counter shop, you press Switch theme twice. What does the Tea shop heading's counter say?

                Choose an answer to see the explanation.

              7. Question 7 of 8A provider's value changes from "light" to "dark". Which components render again because of it?

                Choose an answer to see the explanation.

              8. Question 8 of 8A list passes through a Layout that only places it on the page. What should you try before a context?

                Choose an answer to see the explanation.

              Key takeaways

              • Prop drilling is passing a prop through components that only hand it on. Context lets a value skip them.
              • createContext(defaultValue) makes the context object, outside any component. The object holds no value; the default is used only when no provider is above.
              • Provide a value with <ThemeContext value={…}> (React 19; older code uses .Provider). Read it with useContext(ThemeContext) at the top level, or use(ThemeContext), which also works after an early return or inside an if.
              • A component reads the nearest provider above it, so a provider inside another overrides the value for its part of the tree.
              • When the value changes, every component that reads the context renders again. Before using context, try props, or passing JSX as children.

              Remember the one-liner.
              Provide it once up high; read it anywhere below.

              Coming next: Scaling up with a reducer and context. It puts a reducer's state and its dispatch in context, so any component on the screen can read the state and send actions.

              CompleteFrontend Clear concepts. Working examples.