How does React Context and useContext work?
React Context lets you pass data through the component tree without prop drilling. Create a context, wrap with a Provider, and consume with useContext.
React
Overview
Context solves "prop drilling" - passing props through many levels of components that don't need them, just to get data to a deeply nested child. With Context, you create a value at a high level and any descendant can read it directly, regardless of nesting depth.
Key Points
- •`createContext(defaultValue)` creates the context object.
- •`<Context.Provider value={...}>` makes a value available to all descendants.
- •`useContext(Context)` subscribes a component to the context value.
- •When the Provider's `value` changes, all `useContext` consumers re-render.
- •Context is NOT a replacement for a state manager - it is a delivery mechanism, not a store.
- •Split contexts by concern (ThemeContext, AuthContext) to avoid unnecessary re-renders.
Examples
Theme context end-to-end
tsximport { createContext, useContext, useState } from 'react';
type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light'); // default value
// Provider - wrap the part of the tree that needs access
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>('light');
return (
<ThemeContext.Provider value={theme}>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
Toggle
</button>
{children}
</ThemeContext.Provider>
);
}
// Consumer - any descendant, no matter how deep
function Card() {
const theme = useContext(ThemeContext);
return <div className={`card card--${theme}`}>...</div>;
}Auth context with actions
tsxinterface AuthContextValue {
user: User | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextValue | null>(null);
// Convenience hook with built-in null check
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');
return ctx;
}
// Usage in any descendant
function UserMenu() {
const { user, logout } = useAuth();
return <button onClick={logout}>Log out {user?.name}</button>;
}Pro Tip
Do not reach for Context for every piece of shared state. Start with props; lift state when siblings need it; use Context only when prop drilling spans 3+ levels or when state is genuinely global (current user, theme, locale). For complex state logic, combine Context with `useReducer`.