React useContext Hook
Broadcast global state across component subtrees and eliminate prop drilling using createContext and useContext.
TL;DR
- Share state across deep component trees without tedious intermediate
propdrilling. - Create typed context objects using the
createContextAPI factory function. - Consume active context values reliably with the declarative
useContexthook.
Context Creation and Providing
Create Typed ContextInstantiate context with initial fallback value.
import { createContext } from 'react';
interface ThemeCtx {
theme: 'light' | 'dark';
toggle: () => void;
}
export const ThemeContext =
createContext<ThemeCtx | null>(null);Context Provider WrapperSupply value to all descendants in the component subtree.
export function ThemeProvider({
children,
}: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>(
'light'
);
const toggle = () => {
setTheme((t) =>
t === 'light' ? 'dark' : 'light'
);
};
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}Consuming Context
Direct Hook ConsumptionRead context value inside any child component.
export function ThemeButton() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('Missing Provider');
return (
<Button onClick={ctx.toggle}>
{ctx.theme}
</Button>
);
}Custom Consumer HookEncapsulate null check inside dedicated custom hook.
export function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) {
throw new Error('useTheme outside Provider');
}
return ctx;
}Default Context FallbackProvide sensible default to avoid null assertions.
const LangContext = createContext<string>('en');Multiple and Nested Contexts
Nesting ProvidersStack multiple context providers cleanly at root.
return (
<AuthProvider>
<ThemeProvider>
<NotificationProvider>
<AppRouter />
</NotificationProvider>
</ThemeProvider>
</AuthProvider>
);Scoped Subtree OverrideOverride context value for a specific isolated section.
const override = { theme: 'dark', toggle };
return (
<ThemeContext.Provider value={override}>
<DarkSection />
</ThemeContext.Provider>
);Separating State and DispatchSplit state and dispatch into two contexts to save renders.
const StateCtx = createContext<State>(initialState);
const DispatchCtx = createContext<Dispatch>(() => {});Context Best Practices
Avoid Prop DrillingEliminate passing props across intermediate layout nodes.
// Cleaner architecture with context
function DeepChild() {
const user = useUser();
return <Text>{user.name}</Text>;
}Prefer Composition FirstPass components as children before creating new context.
// Often better than context
<Page layout={<Sidebar user={user} />} />Memoize Context ValuePrevent unnecessary consumer re-renders on parent updates.
const val = useMemo(
() => ({ theme, toggle }),
[theme]
);
return <ThemeContext.Provider value={val} />;Tips
- Always create a custom hook like
useTheme()that wrapsuseContextand throws an error if called outside its Provider. - Keep context values scoped tightly to subtrees that actually require them rather than wrapping the entire
applicationtree unnecessarily.
Warnings
- Every component consuming a context will re-render whenever the Provider
valueprop changes reference. - Do not reach for Context as a substitute for simple component
compositionusing thechildrenprop.
In Practice
Create an auth context providing user credentials and sign-out capabilities to child components.
- Declare AuthContext with typed user details and logout method.
- Build AuthProvider holding active session state.
- Expose useAuth custom hook with runtime guard.
- Consume user session data cleanly in navigation header.
interface AuthCtx {
user: string;
logout: () => void;
}
const AuthContext =
createContext<AuthCtx | null>(null);
export function useAuth() {
const c = useContext(AuthContext);
if (!c) {
throw new Error('Missing AuthProvider');
}
return c;
}
export function UserBadge() {
const { user, logout } = useAuth();
return (
<CardView>
<CardTitle>{user}</CardTitle>
<Button onClick={logout}>Sign Out</Button>
</CardView>
);
}FAQ
Use Context for truly ambient or global information needed by many components across different nesting depths, such as authenticated user sessions, theme modes, or localization settings.
Wrap useContext inside a custom hook that checks if the context value is undefined, throwing an informative developer error explaining that the component must be wrapped in its Provider.
In React 19, you can render