React Context
React Context
Section titled “React Context”Context provides a way to pass data through the component tree without manually threading props at every level. Use it for global state like the current user, theme, or locale.
When to Use Context
Section titled “When to Use Context”Context suits data that many components at different nesting levels need:
- Authentication state (current user)
- UI theme (dark/light)
- Locale / language
- Feature flags
Don’t use Context for state that is local to a subtree — useState with prop passing is simpler.
Creating a Context
Section titled “Creating a Context”import { createContext, useContext, useState, ReactNode } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextValue { theme: Theme; toggleTheme: () => void;}
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }: { children: ReactNode }) { const [theme, setTheme] = useState<Theme>('light');
const toggleTheme = () => setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> );}
export function useTheme(): ThemeContextValue { const context = useContext(ThemeContext); if (!context) throw new Error('useTheme must be used inside ThemeProvider'); return context;}Providing Context
Section titled “Providing Context”Wrap the relevant part of the tree (usually the app root):
import { ThemeProvider } from './context/ThemeContext';
function App() { return ( <ThemeProvider> <Router /> </ThemeProvider> );}Consuming Context
Section titled “Consuming Context”import { useTheme } from '../context/ThemeContext';
function Header() { const { theme, toggleTheme } = useTheme();
return ( <header className={theme}> <button onClick={toggleTheme}> Switch to {theme === 'light' ? 'dark' : 'light'} mode </button> </header> );}Auth Context Pattern
Section titled “Auth Context Pattern”The most common real-world use:
interface User { id: string; name: string; email: string;}
interface AuthContextValue { user: User | null; login: (email: string, password: string) => Promise<void>; logout: () => void; isLoading: boolean;}
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState<User | null>(null); const [isLoading, setIsLoading] = useState(true);
useEffect(() => { // Check for existing session on mount checkSession().then(setUser).finally(() => setIsLoading(false)); }, []);
const login = async (email: string, password: string) => { const user = await authApi.login(email, password); setUser(user); };
const logout = () => { authApi.logout(); setUser(null); };
return ( <AuthContext.Provider value={{ user, login, logout, isLoading }}> {children} </AuthContext.Provider> );}
export const useAuth = () => { const context = useContext(AuthContext); if (!context) throw new Error('useAuth must be used inside AuthProvider'); return context;};Multiple Contexts
Section titled “Multiple Contexts”Compose multiple providers at the root — order matters only if one depends on another:
function App() { return ( <AuthProvider> <ThemeProvider> <RouterProvider router={router} /> </ThemeProvider> </AuthProvider> );}Context vs Zustand / Redux
Section titled “Context vs Zustand / Redux”| Scenario | Recommended |
|---|---|
| Simple global state (theme, user) | Context |
| Frequent updates (counters, forms) | Zustand / Redux |
| Complex state transitions | Redux Toolkit |
| Server state | TanStack Query |
Context re-renders all consumers when the value changes. For frequently-updating state, prefer a dedicated state management library to avoid unnecessary renders.
Avoiding Re-renders
Section titled “Avoiding Re-renders”Split contexts by update frequency:
// Split into separate contexts so consumers only re-render when their data changesconst UserContext = createContext<User | null>(null);const UserActionsContext = createContext<{ logout: () => void } | null>(null);Or memoize the context value:
const value = useMemo(() => ({ theme, toggleTheme }), [theme]);return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;