React - Context API
Context API
Context lets you pass data through the component tree without passing props down manually at every level.
When to use it
- Many components across different nesting levels need the same data (theme, user, i18n).
- You’d otherwise pass the same prop through multiple layers (prop drilling).
- You want a single source of truth for a concern at the app or sub-tree level.
Try it: Theme Context
View source
const { createContext, useContext, useState } = React;
const ThemeContext = createContext('light');
function Toolbar(){
return (
<div style={{display:'flex', gap:8}}>
<ThemedButton>Primary</ThemedButton>
<ThemedButton>Secondary</ThemedButton>
</div>
);
}
function ThemedButton({ children }){
const theme = useContext(ThemeContext);
const styles = theme === 'dark' ? { background:'#111827', color:'#e5e7eb' } : { background:'#e5e7eb', color:'#111827' };
return <button style={{...styles, border:'1px solid #1f2937', padding:'6px 10px', borderRadius:6}}>{children}</button>;
}
function App(){
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<p>Current theme: <strong>{theme}</strong></p>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>Toggle Theme</button>
<div style={{marginTop:10}}>
<Toolbar />
</div>
</ThemeContext.Provider>
);
}
ReactDOM.createRoot(document.getElementById('try-context')).render(<App />);
Syntax primer (for newcomers)
createContext(defaultValue)
: Creates a Context object.<Context.Provider value={...}>
: Makes a value available to descendants.useContext(Context)
: Reads the nearest provider’s value.
Common pitfalls
- Context updates re-render consumers; avoid storing frequently changing big objects.
- Don’t nest excessive providers for unrelated concerns; split contexts.
Vocabulary
- Context: Mechanism to pass data through the tree without props.
- Provider: Component that supplies a context value.
- Consumer: Component reading a context value (via
useContext
).
Prop drilling vs Context
Without Context, props must be threaded through every intermediate component:
// Prop drilling
function App(){
const user = { name: 'Ada' };
return ; // pass down
}
function Layout({ user }){ return ; }
function Sidebar({ user }){ return ; }
function UserBadge({ user }){ return {user.name}; }
With Context, intermediate components don’t need to know about the data:
const UserContext = React.createContext(null);
function App(){
const user = { name: 'Ada' };
return ;
}
function Layout(){ return ; }
function Sidebar(){ return ; }
function UserBadge(){ const user = React.useContext(UserContext); return {user.name}; }
Exercises
- Add a secondary context for language ("en"/"es") and show localized button labels.
- Split theme settings into two contexts: one for the theme value and one for a
toggleTheme()
function. - Lift the provider up a level and verify that deeply nested components can still read the context.
Checks for Understanding
- When would you prefer Context over prop drilling?
- What causes Context consumers to re-render?
Show answers
- When many components at different nesting levels need the same data.
- Changes to the provided value (by identity or content) cause consumers to re-render.