BitlystBitlyst
PostsSeriesTopicsAbout

Bitlyst

Short, diagram-first posts on JavaScript, React & Next.js. Follow via RSS or the newsletter.

© 2026 Mohsen Fallahnejad

PostsSeriesTopicsLinksAboutRSSGitHubX
Posts/react
Intermediate3 min read

React Context Explained: How It Works Under the Hood

Understand what React Context is, how it works internally, and best practices for using it effectively.

Mohsen Fallahnejad

Mohsen Fallahnejad

Oct 6, 2025Discuss

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:

tsx
// Before: prop drilling
<Layout theme="dark">
  <Header theme="dark">
    <Logo theme="dark" />
  </Header>
</Layout>

With Context:

tsx
<ThemeProvider value="dark">
  <Layout />
</ThemeProvider>

⚙️ How Context Works (Under the Hood)#

Step 1 — Create#

tsx
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#

tsx
<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>
  • React stores the value on the Provider’s Fiber node.
  • Keeps track of all child consumers that read from this context.

Step 3 — Consume#

tsx
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 _currentValue reference on its Provider.
  • useContext subscribes the component to that Provider.
  • When the Provider’s value changes, React marks all consumers for re-render.
  • React compares by reference, not by deep value.

So:

tsx
// ❌ 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#

tsx
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#

tsx
function Header() {
  const { mode, toggle } = useTheme();
  return (
    <header className={mode}>
      <button onClick={toggle}>Switch to {mode === "light" ? "dark" : "light"}</button>
    </header>
  );
}

Multiple Contexts#

tsx
<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 useContext subscribes 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#

  1. Memoize values
    tsx
    const value = useMemo(() => ({ user, logout }), [user]);
    
  2. Split context
    tsx
    const UserContext = createContext();
    const ThemeContext = createContext();
    
    → Changing theme won’t re-render user consumers.
  3. Selector pattern Use libraries like use-context-selector for fine-grained subscriptions.
  4. Avoid passing new objects/functions each render.

✅ Summary#

ConceptDescription
ContextShare data deeply without prop drilling
ProviderSets value for all descendants
useContextReads nearest Provider’s value
Triggers re-renderWhen value prop changes by reference
AvoidRapid 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.

Filed under#react#context#hooks#state management

Was this worth your time?

One tap tells me what to write more of.

Mohsen Fallahnejad

Written by

Mohsen Fallahnejad

Frontend engineer explaining JavaScript, React & Next.js internals in posts you can finish over coffee. More about me

X ↗GitHub ↗LinkedIn ↗
← OlderBroadcastChannel API in JavaScriptNewer →React Fiber Explained: How React’s Engine Actually Works

Start the discussion

Questions, corrections, a better approach? I read and reply to every comment.

Read next

React useEffect Dependencies — The Bug That Keeps Coming Back

Why your effect reads old state, fires twice, or loops forever — and the simple rules to fix it (interview-ready).

Jul 8, 20266 min read1

React memo vs useMemo vs useCallback — When to Use Which

Three React APIs that all say 'memoize' — but they do different things. Clear explanations, examples, and a cheatsheet.

Jun 28, 20263 min read

useDeferredValue Explained: Keeping React UIs Responsive

How useDeferredValue works under the hood, when to use it, and how it differs from useTransition.

Dec 12, 20251 min read

Get new posts in your inbox

No spam. Unsubscribe any time.

3 min left
Discuss👍 React
👍