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
tsx
const value = useMemo(() => ({ user, logout }), [user]); - Split context
→ Changing theme won’t re-render user consumers.tsx
const UserContext = createContext(); const ThemeContext = createContext(); - 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.
Start the discussion
Questions, corrections, a better approach? I read and reply to every comment.