Frontend Master

JavaScript Curriculum

Context API
+60 XP

Context API

medium
~30 min·60 XP

The coffee shop app needs theme, user data, and cart state accessible in many components at different depths. Passing these as props through every level becomes unmanageable. Context is the clean solution.

Context API

Context provides a way to share values between components without explicitly passing a prop through every level of the tree. Create a context, provide it once near the top, consume it anywhere below — no matter how deep.

createContext + Provider + useContext

The three-step pattern — create, provide, consume:

context-provider.jsx

Context jumps the tree — consumers read directly from the Provider, no matter how deep they are.

AppcreateContext + Provider
└─
HeaderuseContext(ThemeContext)
└─
Menuno context needed
└─
MenuCardno context needed
└─
CartButtonuseContext(CartContext)
└─
CartSidebaruseContext(CartContext)
Live context demo
ThemeContext
Theme: dark
CartContext
0
items in cart
ℹ️The three steps
(1) `const MyContext = createContext(defaultValue)` — create once, usually in its own file. (2) `<MyContext.Provider value={data}>` — wrap the subtree that needs the data. (3) `const data = useContext(MyContext)` — read from any component inside the Provider.

Prop Drilling vs Context — The Problem and Solution

See how many components needlessly receive props before context:

prop-drilling.jsx
// ❌ Prop drilling — user passed through every level
function App() {
  const [user] = useState({ name: 'Jordan' })
  return <Layout user={user} />          // doesn't need it
}
function Layout({ user }) {
  return <Sidebar user={user} />         // doesn't need it
}
function Sidebar({ user }) {
  return <NavMenu user={user} />         // doesn't need it
}
function NavMenu({ user }) {
  return <UserBadge user={user} />       // finally uses it!
}
// 4 levels of props just to get data to one component
Depth
Appuser={...}
Layoutuser={...}
Sidebar👤 uses user
2 components receive user prop — only 1 uses it
⚠️Context is not free — use it deliberately
Every consumer re-renders when the context value changes. Splitting into multiple contexts (ThemeContext, CartContext, UserContext) means each consumer only re-renders when its specific data changes. One giant AppContext causes everything to re-render on every update.

Theme Context — Live Builder

Toggle theme and font size — all consumers update from one Provider:

theme-context.jsx
Theme
Font size
Context value
{
  theme: "dark",
  fontSize: "md",
  colors: {
    bg: "#0d1117",
    text: "#c9d1d9",
    accent: "#1f6feb",
  }
}
The Grind ☕useContext(ThemeContext)
Espresso
£2.50
Cappuccino
£2.50
All components read from ThemeContext — one Provider, zero prop drilling
💡Context + useReducer = lightweight global state
Combine `useContext` with `useReducer` for app-level state management: the Provider wraps the app, holds the reducer, and provides both `state` and `dispatch`. Any component can read state or dispatch actions without prop drilling. This pattern covers most real-world needs without Redux.

Your Challenge

Build ThemeContext: createContext({ theme: 'dark', toggle: () => {} }). Write ThemeProvider that wraps children with the Provider, holds theme state, and passes { theme, toggle } as value. Create useTheme() custom hook: return useContext(ThemeContext). Use useTheme() in Header and MenuCard.

Challenge

Create a ThemeContext with createContext. Build a ThemeProvider that wraps the app, holds theme state, and provides theme + toggleTheme. Consume it in Header and MenuCard with useContext. No prop drilling needed.

createContextuseContextProvidercontext-valueprop-drillingThemeContextcontext-patterndefault-valueconsumer
Context API | Nexus Learn