React’s Context API lets you share data between components without passing props manually through every level.
It’s designed for global values like theme, language, or authentication — where many components need access to the same data.
🧩 What is React Context?
Context provides a way to share values like theme, user, or locale deeply in the component tree without prop drilling.
It helps solve this problem:
// Before: prop drilling <Layout theme="dark"> <Header theme="dark"> <Logo theme="dark" /> </Header> </Layout> With Context:
<ThemeProvider value="dark"> <Layout /> </ThemeProvider> ⚙️ How Context Works (Under the Hood)
Step 1 — Create
const ThemeContext = React.createContext("light"); - Creates a Context object with two parts:
- A Provider component.
- A reference for the current value inside React’s internal Fiber tree.
Step 2 — Provide
<ThemeContext.Provider value="dark"> <App /> </ThemeContext.Provider> - React stores the
valueon the Provider’s Fiber node. - Keeps track of all child consumers that read from this context.
Step 3 — Consume
const theme = useContext(ThemeContext); - React looks upward in the component tree to find the nearest Provider.
- Returns its value.
- If the Provider’s value changes, React re-renders all consumers that depend on it.
🧠 What Happens Internally
- Every Context keeps a
_currentValuereference on its Provider. -
useContextsubscribes the component to that Provider. - When the Provider’s
valuechanges, React marks all consumers for re-render. - React compares by reference, not by deep value.
So:
// ❌ Causes re-renders each time <ThemeContext.Provider value={{ mode: "dark" }}>...</ThemeContext.Provider> // ✅ Stable reference const theme = useMemo(() => ({ mode }), [mode]); <ThemeContext.Provider value={theme}>...</ThemeContext.Provider> 🧱 How to Use Context
Create a Context + Provider
import { createContext, useContext, useState, useMemo } from "react"; const ThemeContext = createContext(); export function ThemeProvider({ children }) { const [mode, setMode] = useState("light"); const value = useMemo( () => ({ mode, toggle: () => setMode(m => (m === "light" ? "dark" : "light")) }), [mode] ); return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>; } export const useTheme = () => useContext(ThemeContext); Use it in Components
function Header() { const { mode, toggle } = useTheme(); return ( <header className={mode}> <button onClick={toggle}>Switch to {mode === "light" ? "dark" : "light"}</button> </header> ); } Multiple Contexts
<AuthProvider> <ThemeProvider> <App /> </ThemeProvider> </AuthProvider> 🔍 When (and When Not) to Use Context
✅ Use Context for:
- Theme, locale, user, config
- Shared but stable global data
- App-wide settings
⚠️ Avoid for:
- Rapidly changing values (inputs, lists, timers)
- High-frequency updates — causes re-renders in all consumers
Use state libraries (Zustand, Redux, Jotai, etc.) or split contexts.
🧠 How React Handles Context Internally (Fiber)
- Each Provider is a node in the Fiber tree.
- Context changes propagate downward through fibers.
- Each consumer using
useContextsubscribes to that Provider. - On update, React traverses the subtree to re-render affected components.
- React guarantees consistency: all consumers see the same value in one render pass.
🚀 Optimization Tips
- Memoize values
const value = useMemo(() => ({ user, logout }), [user]); - Split context
const UserContext = createContext(); const ThemeContext = createContext(); → Changing theme won’t re-render user consumers.
- Selector pattern Use libraries like
use-context-selectorfor fine-grained subscriptions. - Avoid passing new objects/functions each render.
✅ Summary
| Concept | Description |
|---|---|
| Context | Share data deeply without prop drilling |
| Provider | Sets value for all descendants |
| useContext | Reads nearest Provider’s value |
| Triggers re-render | When value prop changes by reference |
| Avoid | Rapid updates or non-memoized objects |
✨ React Context = global data flow with React’s rendering guarantees.
Used right, it’s powerful and efficient — used wrong, it becomes a hidden re-render trap.
Originally published on Bitlyst.
