How React Context works under the hood
What is React Context?
React Context is a built-in feature that lets you share values (state, functions, etc.) across the entire component tree without passing props manually at every level.
It’s often used for things like:
- Theme (dark/light)
- Auth user data
- Language or locale
- Toggle states, config settings
Under the Hook: How React Context Works
At a high level:
- You create a context object
- You wrap your component tree with a Provider and pass it a value
- Any descendant component can “consume” that value via
useContext
- Wrap Your App with the context
Think of it like a global container of state available to all child components within a certain scope.
Let’s Build It Step-by-Step
Step 1: Create a Context
import React, { createContext, useState } from 'react';
// Create the context (a container)
const ThemeContext = createContext();
What happens here:
ThemeContext
is an object with two components:
ThemeContext.Provider
: where you define the valueThemeContext.Consumer
: an older way to read the value
Step 2: Create a Provider Component
This is the “source of truth” that stores the data and shares it.
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () =>
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
What happens under the hood:
- When the value (
{ theme, toggleTheme }
) changes, all children using this context re-render automatically. - React keeps a reference to that value and tracks which children are listening for changes.
Step 3: Use Context in Children
import React, { useContext } from 'react';
import { ThemeContext } from './theme-context';
const Header = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<header style={{ background: theme === 'light' ? '#fff' : '#333' }}>
<p>Current theme: {theme}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</header>
);
};
What does useContext
really do?
When you call useContext(SomeContext)
, React:
- Walks up the component tree to find the nearest matching
<SomeContext.Provider>
- Reads its
value
- Subscribes your component to that context’s updates
- Re-renders your component only if the context value changes
React uses reference equality to detect value changes. So if the value is a new object or function, components will re-render.
Step 4: Wrap Your App
import React from 'react';
import ReactDOM from 'react-dom';
import { ThemeProvider } from './theme-context';
import App from './App';
ReactDOM.render(
<ThemeProvider>
<App />
</ThemeProvider>,
document.getElementById('root')
);
Here’s what React Context API really does behind the scenes:
// Pseudocode of how React context might be implemented
const MyContext = React.createContext(defaultValue);
// Somewhere in your tree
<MyContext.Provider value={someValue}>
<Child />
</MyContext.Provider>
// React stores the value in an internal fiber field
// Then inside a component that does:
const value = useContext(MyContext);
// React walks up the fiber tree to find the nearest Provider
// When the value changes, it re-renders all the consuming components
Common Gotchas
- Context Re-renders Everything Using It
- Even if one value inside
value={{ a, b }}
changes, all components using the context re-render - Solution: Split context or memoize the value using
useMemo
const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
2. Don’t Use for High-Frequency Updates
- Avoid using context for things like animation frame updates or mouse position — it’s not performant
- Use local state or other tools like Zustand or Redux
Summary
createContext()
Creates a shared state containerProvider
Supplies the context value to childrenuseContext()
Lets you access that value in any child.- Under the hood React uses internal fiber links + subscriptions
- Optimize with
useMemo
Prevents re-renders on shallow value change.