Stop 11 · State ManagementTopics · 2/5

Context API

Part of the Full Stack Developer Roadmap

In short

You already know how Context works. The real question is when it's enough to run your app's state, and when it starts slowing your app down.

React's built-in mechanism for sharing client state (theme, auth status) across the component tree — a good fit for low-frequency updates without adding a library.

The jaadu mascot sends one message and every window of an apartment block lights up with buzzing phones.

Think of it like this

The society WhatsApp group

Post one message about the water tanker and every phone in the building buzzes, even for people who don't care. Context works the same way: when its value changes, every component reading it re-renders, whether it needed to or not.

Watch it happen

One change, every reader re-renders

AppContext
ThemeToggle
ProductList
CartBadge
Footer

1/5AppContext holds { theme, cart }. ThemeToggle, ProductList and CartBadge all read it.

The big ideas

What you need to know

Good for slow-changing data

Theme, language, the logged-in user. Things that change rarely but are read in many places.

Pair it with useReducer

Put a reducer's state and dispatch in context and you get a mini Redux with no library at all.

Split by how often it changes

Keep fast-changing values, like a cart, in a separate context from slow ones, like the theme.

Know when to move on

If many components update often, or you want devtools and middleware, try Zustand or Redux Toolkit.

See it in code

Walk through the code

tsx
1import { createContext, useContext, useReducer, type Dispatch, type ReactNode } from "react";2 3type State = { items: string[] };4type Action = { type: "add"; item: string } | { type: "clear" };5 6function reducer(state: State, action: Action): State {7  if (action.type === "add") return { items: [...state.items, action.item] };8  return { items: [] };9}10 11const CartContext = createContext<{ state: State; dispatch: Dispatch<Action> } | null>(null);12export function CartProvider({ children }: { children: ReactNode }) {13  const [state, dispatch] = useReducer(reducer, { items: [] });14  return <CartContext value={{ state, dispatch }}>{children}</CartContext>;15}16export const useCart = () => useContext(CartContext)!;

1/4The reducer works out the next cart from the current one and an action.

Any component inside CartProvider can call useCart() to read items or dispatch({ type: "add", item }). It's a small Redux-style store built only from React.

Save yourself the pain

Common mistakes

Oops

Putting user, theme, cart and notifications into one AppContext.

Do this instead

One change re-renders every reader. Split it up, so a new notification doesn't re-render the theme toggle.

Oops

Passing a brand-new value object from a provider that re-renders often.

Do this instead

Wrap the value in useMemo, or let the React Compiler handle it, so readers don't re-render for nothing.

Your mission

Count the re-renders

~25 min

Quick check

A ThemeToggle reads AppContext, which holds { theme, cart }. You add an item to the cart. Does ThemeToggle re-render?

Go deeper

  1. 1Build a theme or auth context and consume it in a deeply nested component.
  2. 2Notice performance issues when a context value changes very frequently.
  3. 3Learn to split contexts by concern rather than one giant global context.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge