useContext
Share data across the component tree without prop drilling
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
Context lets you pass data through the component tree without .
Create and provide context:
jsx import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext('light'); // default value function App() { const [theme, setTheme] = useState('dark'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <MainLayout /> </ThemeContext.Provider> ); }
Consume context anywhere in the tree:
jsx function Navbar() { const { theme, setTheme } = useContext(ThemeContext); return ( <nav className={theme}> <button onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')}> Toggle theme </button> </nav> ); }
When to use Context:
- Theme (dark/light)
- Current user / authentication
- Language/locale
- Global UI state (sidebar open/closed)
When NOT to use Context:
- Frequently changing data (causes all consumers to re-render)
- Data that isn't truly global (lift state instead)
- As a full state management replacement — use Zustand, Redux, etc. for complex state
Context doesn't replace prop drilling for component libraries — explicit props are more debuggable.
Examples
Auth context pattern
Wrapping useContext in a custom hook is the standard pattern
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (email, password) => {
const u = await apiLogin(email, password);
setUser(u);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);
// Usage in any component:
function ProfileButton() {
const { user, logout } = useAuth();
return user ? <button onClick={logout}>{user.name}</button> : null;
}Context with useReducer for complex state
useContext + useReducer is a lightweight Redux-like pattern for moderate complexity
const CartContext = createContext(null);
function cartReducer(state, action) {
switch (action.type) {
case 'ADD': return [...state, action.item];
case 'REMOVE': return state.filter(i => i.id !== action.id);
default: return state;
}
}
export function CartProvider({ children }) {
const [cart, dispatch] = useReducer(cartReducer, []);
return (
<CartContext.Provider value={{ cart, dispatch }}>
{children}
</CartContext.Provider>
);
}How well did you understand this?
Next in React
Custom Hooks