What are some pitfalls about using context in React?
TL;DR
Context in React is convenient but easy to misuse. The biggest pitfalls are passing a fresh object or array as the provider value on every render, assuming memo or React Compiler will stop a context subscription update (they won't), and putting frequently changing, unrelated data into one context. Split independent values into focused providers, stabilize object values when appropriate, and consider a selector-based state library when consumers need different slices of rapidly changing state.
Pitfalls of using context in React
Context is convenient for distribution, but its broadcast update model and implicit dependencies can become costly when a value changes frequently or grows too broad.
Unnecessary re-renders from an unstable provider value
When a context value changes by reference, every component that reads that context re-renders — even if it only uses a field that hasn't actually changed. The most common cause is constructing a new object inline as the provider's value, which makes a fresh reference on every parent render:
// Pitfall — `value` is a new object every render, so every consumer re-rendersfunction ParentComponent() {const [user, setUser] = useState(null);const [theme, setTheme] = useState('light');return (<MyContext.Provider value={{ user, setUser, theme, setTheme }}><ChildComponent /></MyContext.Provider>);}
Fix it by memoizing the value. React Compiler may generate equivalent memoization when enabled, but Context still propagates every genuine value change:
function ParentComponent() {const [user, setUser] = useState(null);const [theme, setTheme] = useState('light');const value = useMemo(() => ({ user, setUser, theme, setTheme }),[user, theme],);return (<MyContext.Provider value={value}><ChildComponent /></MyContext.Provider>);}
Note: only consumers of the context re-render when the value changes — not "all components in the subtree." Components that don't call useContext/use(MyContext) are unaffected.
React.memo doesn't stop context-driven re-renders
A common surprise: wrapping a consumer in React.memo does not prevent re-renders triggered by a context value change. memo only skips re-renders caused by changing props. If the component reads a context whose value changed, it re-renders regardless. The fix is to make the context value stable (above) or split the context.
Putting too much unrelated state in one context
If you cram an entire app's state into a single context, every change to any slice re-renders every consumer. Split it into smaller, focused providers — for example, separate AuthContext, ThemeContext, and CartContext — so that a cart update doesn't re-render every theme consumer. You can also split read and write APIs into separate contexts so components that only need to dispatch don't re-render when state changes.
No built-in selectors
Unlike Redux's useSelector, React context has no built-in way to subscribe to a slice of the value. Any change to the value re-runs every consumer. Workarounds include:
- Splitting the context into smaller pieces (preferred).
- The community
use-context-selectorlibrary, which adds selector-based subscriptions.
Using context as a state manager
Context transports a value through a subtree; pairing it with useState or useReducer can be a reasonable state solution for a small application. Context does not add caching, middleware, devtools, or selector-based subscriptions by itself. Consider Redux Toolkit, Zustand, or Jotai when those capabilities solve a concrete client-state problem. Fetched data can be passed through context, but a framework data layer or a library such as TanStack Query, SWR, or RTK Query is usually a better fit when it needs caching, deduplication, invalidation, or background refetching.
Debugging difficulties
Because context updates can fan out across the tree, tracking down which provider caused a re-render can be hard, especially with nested providers. The React DevTools "Profiler" tab and "Why did this render?" highlighting help here, but it's still a good reason to keep providers small and focused.
React 19: use(Context) as an alternative to useContext
In React 19 you can read a context with the use API. Despite its name, use is not a Hook. Unlike useContext, it can be called inside conditionals and loops, but it has the same context subscription behavior:
import { use } from 'react';function Profile() {const user = use(UserContext);return <div>{user.name}</div>;}
useContext still works and is not going away — use(Context) is just more flexible.
Further reading
- Passing data deeply with context
useAPI referenceReact.memo- Redux Toolkit
- Zustand
- Jotai
use-context-selector