Why isn't Context always suitable for global state?
The key idea
React Context is not the same as a global store, it is designed as a mechanism for passing data down the tree without prop drilling, not as a full-fledged state-management system.
Why Context is not always suitable for global state
1. Every change to value re-renders all consumers
When you write:
<MyContext.Provider value={{ user, theme }}>
<App />
</MyContext.Provider>React compares value by reference.
If it has changed -> every component using useContext(MyContext) re-renders, even if the field it needs did not change.
On a large tree, this causes cascading updates and lag.
Example:
user.name changes -> the whole UI using theme re-renders too.
2. There are no "selectors" (granular subscriptions)
Context cannot subscribe a component to only part of the data.
If
valueholds{ user, theme, cart }, then a change tocartupdates even the components that only needtheme.
The workaround is to use third-party tools (use-context-selector, Zustand, Jotai),
but "plain" Context does not provide this.
3. Every update goes through a React render
Context is not optimized for frequently changing state (for example, a cursor, a timer, text input). At 60 updates per second, React simply cannot keep up.
Context is UI state, not a "reactive stream".
4. There are no tools, middleware, or devtools
Context:
- has no devtools;
- does not support time-travel;
- does not make it convenient to log changes, roll back state, or debug business logic;
- does not scale with a complex architecture.
5. Hard to scale as the app grows
As the app grows,
"crutches" start appearing: dozens of contexts (AuthContext, CartContext, UIContext, SettingsContext...), nested Provider trees, tangled dependencies, and "Provider hell":
<AuthProvider>
<ThemeProvider>
<UIProvider>
<CartProvider>
<App />
</CartProvider>
</UIProvider>
</ThemeProvider>
</AuthProvider>Hard to maintain, test, and extend.
6. Context is synchronous and local
It is not designed for:
- asynchronous requests (fetch/cache/retry);
- syncing with a server;
- offline data;
- reactive subscriptions (WebSocket, SSE, etc.).
For that, React Query, Zustand, Redux Toolkit, and others are a better fit.
When Context is still ideal
| Use case | Why it fits |
|---|---|
| Theme (light/dark) | Simple, rare change |
| Interface language (i18n) | Updates only when the language is switched |
| Current user | Changes only on login/logout |
| Feature flags, settings | Small amount of data, low change frequency |
| Compound components | Context is used as an internal communication bus (Tabs, Modal, Dropdown, etc.) |
Alternatives for "real" global state
| Task | Tool |
|---|---|
| Frequently changing state, business logic | Zustand, Jotai, Redux Toolkit |
| Asynchronous data (fetch, cache) | TanStack Query (React Query) |
| Reactive subscriptions, server pushes | RxJS, useSyncExternalStore, a custom store |
| Simple static data | Context |
Summary
| Reason | Why Context does not fit |
|---|---|
| Frequent updates | All consumers re-render |
| No selectors | You cannot subscribe to just part of the data |
| No tooling | You cannot debug or control the flow |
| Poor scalability | Provider hell and complex dependencies |
| No asynchrony | Everything is synchronous and local |
Conclusion:
Context is a mechanism for "passing data down the tree", not a "global state store".
For small global flags, it's great. For complex business state, Zustand, Jotai, Redux Toolkit, or React Query are better.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.