# Context API

Course: [React](https://www.devtutorials.ai/learn/react) · Section: State Sharing
Source: https://www.devtutorials.ai/learn/react/state-sharing/context-api

> Why context exists, prop drilling, creating context, Providers, reading with useContext, sharing state across components, when to use context, global state basics, a theme example and performance tips.

## Purpose of the Context API

The **Context API** is a React feature that lets a parent share a value with **every** component below it, without passing it as a prop through each level.

It is used for data that many components need, such as the logged-in user, the theme, or the language of the app.

### Real-World Example

A bookshop app has a light and a dark theme. The header, the book cards and the footer all need to know the current theme, and they are many levels below `App`.

Without context, every component in between must receive the theme as a prop only to pass it on. With context, `App` shares the theme once, and each component that needs it reads it directly.

Context shares a value with all components inside a Provider. Any of them can read it directly, at any depth.

## Prop Drilling

**Prop drilling** means passing a prop through many components that do not use it, just so it can reach a component deep down.

<figure className="react-fig">
<svg viewBox="0 0 520 110" role="img" aria-labelledby="fig-x-drill" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-x-drill">Prop drilling: user is passed through Page, Sidebar and Menu, which do not use it, to reach UserBadge</title>
  <rect x="5" y="30" width="80" height="48" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="45.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>App</text>
  <text x="45.0" y="68.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>has user</text>
  <path d="M85 54 L97 54" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M97 54 L88.7 57.5 L88.7 50.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="101" y="30" width="80" height="48" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="141.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Page</text>
  <text x="141.0" y="68.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>passes user</text>
  <path d="M181 54 L193 54" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M193 54 L184.7 57.5 L184.7 50.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="197" y="30" width="80" height="48" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="237.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Sidebar</text>
  <text x="237.0" y="68.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>passes user</text>
  <path d="M277 54 L289 54" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M289 54 L280.7 57.5 L280.7 50.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="293" y="30" width="80" height="48" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="333.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Menu</text>
  <text x="333.0" y="68.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>passes user</text>
  <path d="M373 54 L385 54" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M385 54 L376.7 57.5 L376.7 50.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="389" y="30" width="126" height="48" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="452.0" y="51.0" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>UserBadge</text>
  <text x="452.0" y="68.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>uses user</text>
  <circle r="6" style={{ fill: "var(--bad)" }}>
    <animateMotion dur="3s" repeatCount="indefinite" path="M45 54 L97 54 L193 54 L289 54 L452 54" />
  </circle>
</svg>
<figcaption>Three components pass user along without using it. This is called prop drilling.</figcaption>
</figure>

It works, but look at `Page`, `Sidebar` and `Menu`. Each one takes `user` and passes it on, without using it. Problems with this:

- **More code** — Every middle component needs the prop and its type.
- **Hard to change** — Adding a new value means editing every component on the way.
- **Hard to read** — It is not clear which components really use the value.

Context solves this.

## Creating Context

Using context takes three steps.

<figure className="react-fig">
<svg viewBox="0 0 520 100" role="img" aria-labelledby="fig-x-steps" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-x-steps">Three steps of context: createContext, wrap with a Provider, read with useContext</title>
  <rect x="5" y="25" width="160" height="54" rx="8" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="85.0" y="49.0" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>1. createContext</text>
  <text x="85.0" y="66.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>make the context</text>
  <path d="M165 52 L183 52" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M183 52 L174.7 55.5 L174.7 48.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="187" y="25" width="160" height="54" rx="8" style={{ fill: "var(--info-soft)", stroke: "var(--info)", strokeWidth: 1.5 }} />
  <text x="267.0" y="49.0" textAnchor="middle" style={{ fill: "var(--info)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>2. Provider</text>
  <text x="267.0" y="66.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>share a value</text>
  <path d="M347 52 L365 52" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M365 52 L356.7 55.5 L356.7 48.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="369" y="25" width="146" height="54" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="442.0" y="49.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>3. useContext</text>
  <text x="442.0" y="66.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>read the value</text>
  <circle r="6" style={{ fill: "var(--accent-secondary)" }}>
    <animateMotion dur="2s" repeatCount="indefinite" path="M165 52 L183 52 M347 52 L365 52" />
  </circle>
</svg>
<figcaption>Create it once, provide it near the top, and read it anywhere below.</figcaption>
</figure>

The first step is to **create** the context with `createContext`. You do this once, in its own file, and export it.

```tsx
import { createContext } from "react";

interface User {
  name: string;
}

export const UserContext = createContext<User | null>(null);
```

- `createContext` makes a new context.
- `<User | null>` is the type of the shared value: a user, or `null` when nobody is signed in.
- `null` is the **default value**. A component gets it only if there is no Provider above it.

## Context Provider

The second step is to **provide** a value. Wrap part of your app in the context's `Provider`, and give it a `value`.

```tsx
<UserContext.Provider value={user}>
  <Page />
</UserContext.Provider>
```

Every component inside the Provider, at any depth, can now read `user`. Components outside it cannot.

Put the Provider as low as you can, but above every component that needs the value. For app-wide data, that is usually near the top, around `App`'s content.

## Reading Context with `useContext`

The third step is to **read** the value. In any component inside the Provider, call `useContext` with the context.

```tsx
const user = useContext(UserContext);
```

Here is the drilling example again, with context. The middle components now pass nothing.

<figure className="react-fig">
<svg viewBox="0 0 520 190" role="img" aria-labelledby="fig-x-ctx" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-x-ctx">With context, the Provider sends user straight to UserBadge. The middle components pass nothing</title>
  <rect x="5" y="20" width="110" height="52" rx="8" style={{ fill: "var(--info-soft)", stroke: "var(--info)", strokeWidth: 1.5 }} />
  <text x="60.0" y="43.0" textAnchor="middle" style={{ fill: "var(--info)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>App</text>
  <text x="60.0" y="60.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>the Provider</text>
  <rect x="130" y="20" width="90" height="52" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="175.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Page</text>
  <rect x="235" y="20" width="90" height="52" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="280.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Sidebar</text>
  <rect x="340" y="20" width="70" height="52" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="375.0" y="51.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Menu</text>
  <rect x="425" y="20" width="90" height="52" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="470.0" y="43.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>UserBadge</text>
  <text x="470.0" y="60.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>useContext</text>
  <path d="M115 46 L128 46" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none", strokeDasharray: "5 4" }} />
  <path d="M128 46 L119.7 49.5 L119.7 42.5 Z" style={{ fill: "var(--faint)" }} />
  <path d="M220 46 L233 46" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none", strokeDasharray: "5 4" }} />
  <path d="M233 46 L224.7 49.5 L224.7 42.5 Z" style={{ fill: "var(--faint)" }} />
  <path d="M325 46 L338 46" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none", strokeDasharray: "5 4" }} />
  <path d="M338 46 L329.7 49.5 L329.7 42.5 Z" style={{ fill: "var(--faint)" }} />
  <path d="M410 46 L423 46" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none", strokeDasharray: "5 4" }} />
  <path d="M423 46 L414.7 49.5 L414.7 42.5 Z" style={{ fill: "var(--faint)" }} />
  <path d="M60 72 Q260 190 470 72" style={{ fill: "none", stroke: "var(--info)", strokeWidth: 2.5 }} />
  <text x="265" y="150" textAnchor="middle" style={{ fill: "var(--info)", fontSize: 12, fontWeight: 600, fontFamily: "var(--sans)" }}>context: no props needed in between</text>
  <circle r="6" style={{ fill: "var(--info)" }}>
    <animateMotion dur="2.2s" repeatCount="indefinite" path="M60 72 Q260 190 470 72" />
  </circle>
</svg>
<figcaption>The Provider shares the value. Any component inside it can read it directly.</figcaption>
</figure>

  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

`Page`, `Sidebar` and `Menu` have no props now. `UserBadge` reads the user straight from the context.

Look at this line in `UserBadge`:

```tsx
{user ? user.name : "guest"}
```

The type is `User | null`, so `user` may be `null`. The `? :` shows "guest" in that case.

Try removing the `<UserContext.Provider>` lines in `App` and press ▶ Run. The badge shows "guest", because without a Provider you get the default value, `null`.

## Sharing Data Across Components

Context can share **state**, not only fixed values. Put the state in the component that holds the Provider, and pass both the value and a function to change it.

Any component inside can then **read** and **change** the shared state.

      <option value="en">English</option>
      <option value="hi">हिन्दी</option>
    </select>
  );
}

  const [language, setLanguage] = useState<Language>("en");

  return (
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Pick हिन्दी from the list. The header and the cart message both change. `LanguagePicker` changed the shared state, and every reader updated.

Look at the Provider:

```tsx
<LanguageContext.Provider value={{ language, setLanguage }}>
```

The value is an object with two parts: the current `language`, and `setLanguage` to change it. The double braces are an object `{ }` inside a TSX expression `{ }`.

## When to Use Context

Context is useful, but it is not for every piece of data.

**Use context when:**

- many components at **different levels** need the same value, and
- the value is **app-wide**, such as the user, theme, language or cart.

**Do not use context when:**

- only one or two nearby components need the value. Pass props instead.
- the value changes **very often**, such as text typed in a box. Every reader would re-render on every key press.

Try props first. If you pass the same prop through three or more components that do not use it, consider context.

## Global State Basics

**Global state** is state that the whole app can reach. With context, you make global state in three steps:

1. Keep the state with `useState` (or `useReducer`) in a component near the top.
2. Share it, and the functions that change it, through a Provider.
3. Read it anywhere with `useContext`.

A clean way to do this is to put all three in one file: the context, a **Provider component**, and a small **custom hook** to read it.

      {children}
  );
}

// 2. A custom hook to read it
  const cart = useContext(CartContext);
  if (cart === null) {
    throw new Error("useCart must be used inside <CartProvider>");
  }
  return cart;
}
`,
    "App.tsx": `import { CartProvider, useCart } from "./CartContext";

function CartIcon() {
  const { items } = useCart();
  return <p>Cart ({items.length})</p>;
}

function BookCard({ title }: { title: string }) {
  const { addItem } = useCart();
  return <p>{title} <button onClick={() => addItem(title)}>Add</button></p>;
}

function Checkout() {
  const { items, clear } = useCart();
  return (
    <p>
      {items.length === 0 ? "Nothing to buy yet." : "Buying: " + items.join(", ")}{" "}
      {items.length > 0 && <button onClick={clear}>Clear</button>}
    </p>
  );
}

  return (
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click **Add** on the books. The cart icon and the checkout line both update. Here is why this pattern is useful:

- `App` only wraps everything in `<CartProvider>`. It does not hold any cart code.
- Each component calls `useCart()` and takes only what it needs.
- If someone forgets the Provider, `useCart` shows a clear error message instead of a confusing bug.

## Theme Example

A **theme** decides the colours of the app, such as light or dark. It is a classic use of context, because nearly every component needs it.

    </div>
  );
}

  return (
  );
}
`,
    "App.css": `.page { padding: 12px; border-radius: 8px; }
.page.light { background: #f8fafc; }
.page.dark { background: #0f172a; }
.card { margin-top: 8px; padding: 10px 12px; border-radius: 6px; }
.card.light { background: #ffffff; color: #0f172a; border: 1px solid #cbd5e1; }
.card.dark { background: #1e293b; color: #f8fafc; border: 1px solid #334155; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click the button. The page and both cards change colour together, because they all read the same `theme` from context.

## Context Performance Considerations

When the Provider's value changes, **every** component that reads that context re-renders. This is usually fine. In big apps, it can make things slow.

<figure className="react-fig">
<svg viewBox="0 0 520 160" role="img" aria-labelledby="fig-x-perf" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-x-perf">When the Provider value changes, every component that reads the context re-renders</title>
  <rect x="170" y="10" width="180" height="46" rx="8" style={{ fill: "var(--info-soft)", stroke: "var(--info)", strokeWidth: 1.5 }} />
  <text x="260.0" y="30.0" textAnchor="middle" style={{ fill: "var(--info)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Provider</text>
  <text x="260.0" y="47.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>value changes</text>
  <path d="M220 56 L90 104" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M90 104 L96.6 97.8 L99.0 104.4 Z" style={{ fill: "var(--faint)" }} />
  <path d="M260 56 L260 104" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M260 104 L256.5 95.7 L263.5 95.7 Z" style={{ fill: "var(--faint)" }} />
  <path d="M300 56 L430 104" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M430 104 L421.0 104.4 L423.4 97.8 Z" style={{ fill: "var(--faint)" }} />
  <rect x="30" y="106" width="120" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }}>
    <animate attributeName="opacity" values="1;0.35;1" dur="1.6s" repeatCount="indefinite" />
  </rect>
  <text x="90" y="131" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>re-renders</text>
  <rect x="200" y="106" width="120" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }}>
    <animate attributeName="opacity" values="1;0.35;1" dur="1.6s" repeatCount="indefinite" />
  </rect>
  <text x="260" y="131" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>re-renders</text>
  <rect x="370" y="106" width="120" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }}>
    <animate attributeName="opacity" values="1;0.35;1" dur="1.6s" repeatCount="indefinite" />
  </rect>
  <text x="430" y="131" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>re-renders</text>
</svg>
<figcaption>Every reader of a context updates together. Keep context values small and stable.</figcaption>
</figure>

      <button onClick={() => setCount(count + 1)}>Change context</button>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Clear the console, then click **Change context**. Only "Header" and "Cart" render again, because they read the context. `NotReader` does not.

`memo` tells React to skip a component when its props have not changed. You will learn it in a later lesson.

Tips to keep context fast:

- **Split contexts** by topic. Keep `ThemeContext` and `CartContext` separate, so a cart change does not re-render theme readers.
- **Do not put fast-changing values** in context, such as typed text or mouse position.
- **Keep the Provider low** in the tree, around only the components that need it.

## Common mistakes

<div className="tscroll">

| Mistake | What you see | Fix |
|---|---|---|
| Forgetting the Provider | You always get the default value | Wrap the components in `<MyContext.Provider value={...}>` |
| Reading context in a component outside the Provider | You get the default value | Move the component inside the Provider |
| Passing `value={language}` but reading `{ language }` | `language` is `undefined` | Pass and read the same shape: `value={{ language }}` |
| Creating the context inside a component | A new context every render, so readers lose the value | Create it once, at the top of a file |
| One big context for everything | Small changes re-render many components | Split it into several small contexts |
| Using context for data only one child needs | Extra code for no benefit | Pass a prop instead |

</div>

## Check Yourself

## Exercises

## What to Remember

- Context shares a value with every component inside its Provider, so you avoid prop drilling.
- Three steps: `createContext`, wrap with a `Provider` and a `value`, then read with `useContext`.
- Use context for app-wide data. Split it by topic, and keep fast-changing values out of it.

Next: Client-Side Routing
