React useContext Hook
The React useContext hook lets components read shared data without passing props through every level of the tree. Passing props through components that do not need them is called prop drilling, and Context is React's built-in solution.
Context is ideal for app-wide values like the current theme, the logged-in user, or the selected language.
Step 1: Create a Context
The argument is the default value, used only when a component has no matching Provider above it.
// ThemeContext.js
import { createContext } from 'react';
export const ThemeContext = createContext('light');(a context with default value "light")
Step 2: Provide a Value
Wrap part of your tree in the Provider. Every component inside can read its value, no matter how deep.
import { useState } from 'react';
import { ThemeContext } from './ThemeContext.js';
export default function App() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Layout />
</ThemeContext.Provider>
);
}(Layout and all its children can now access theme)
Step 3: Read the Value with useContext
ThemeToggle can sit five levels deep inside Layout and still read and change the theme directly. When the Provider value changes, every component using that context re-renders.
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext.js';
function ThemeToggle() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Current theme: {theme}
</button>
);
}Current theme: dark (click) Current theme: light
A Reusable Provider Component
A common pattern is to wrap the provider and a custom hook in one file so the rest of the app never touches the context object directly.
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (name) => setUser({ name });
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}(any component can call useAuth() to get user, login and logout)
When to Use Context
- Good: theme, auth user, locale, feature flags — data many components need.
- Avoid for values that change many times per second; every consumer re-renders.
- Do not use it just to skip passing props one or two levels; plain props are clearer.
- For large, complex global state consider useReducer with context, Zustand or Redux Toolkit.
Frequently asked questions
What is prop drilling in React?
Passing props through several intermediate components that do not use them, just so a deeply nested component can receive them. Context removes this need.
Is the React Context API a replacement for Redux?
For simple shared state, yes. Redux and similar libraries add devtools, middleware and optimized subscriptions that help in large apps with complex state.
Why is my useContext returning undefined?
The component is not inside the matching Provider, or you imported a different context object. Check that the Provider wraps it and that both use the same exported context.