skip to content

Context vs External Store

Context is a dependency-injection channel, not a state manager; once you need selector-level subscriptions or updates driven from outside React, an external store fits better. Interviewers ask 'when would you swap context for Redux or Zustand?' and want the tradeoff, not a preference.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

4

Is React Context a state-management library? Explain what Context actually provides, and what it does not provide compared with an external store such as Redux or Zustand.

level: juniorimportance: must knowfreq 70%

answer

  1. a channel, not a container
  2. solves prop drilling, nothing else
  3. no subscribe, no selector
  4. state still lives in useState or a store
  5. inject the store through it

basics

~20 s

React Context is a dependency-injection channel, not a state manager: it carries one value down the tree so descendants can read it without prop drilling. It stores nothing itself — the state still lives in useState, useReducer, or an external store.

solid answer

~50 s

No — Context is a dependency-injection channel, not a state manager. `createContext` creates the channel, a provider publishes one value to the subtree below it, and `useContext` (or `use(Context)` in React 19) reads whatever the nearest provider published. The state itself lives somewhere else: usually `useState` or `useReducer` inside the provider component, or in an external store. What Context deliberately does not give you is store behaviour — no subscription API, no selector so a consumer can react to a single field, no way to read or write the value from code outside the component tree, and no middleware or devtools timeline. So Context solves prop drilling; a store solves state ownership and fine-grained subscription. They compose rather than compete: it is common to put a store instance *into* Context and let components subscribe to the store.

go deeper

for a junior

Be able to say that Context passes a value down the tree so you stop threading props through intermediate components, and that the state itself still lives in a hook like useState.

for a middle

Explain the mechanics: the provider publishes a value, readers take it from the nearest provider above, and a missing provider falls back to the createContext default. Name the capabilities Context lacks — subscription and selectors.

for a senior

An interviewer expects you to reject the false dichotomy and describe the composition: Context injects a stable store instance, the store owns state and serves fine-grained subscriptions. Justify it with what breaks otherwise.

for a principal

Own the architectural line: Context is the dependency-injection seam of a React codebase, so what you publish through it becomes an interface other teams code against. Argue for publishing stable handles rather than churning values.

## The short version Context is a transport mechanism. A store is an owner of state. Treating them as competitors is the mistake behind the perennial "Context vs Redux" question — they sit at different layers, and a real app often uses both. ## What Context actually is, mechanically `createContext(defaultValue)` returns a context object. Rendering it as a provider publishes a value to everything below it in the tree: ```jsx const ThemeContext = createContext('light'); // React 19: the context renders as the provider directly <ThemeContext value={theme}> <App /> </ThemeContext> ``` `<ThemeContext.Provider value={theme}>` still works and is what you will see in older code. Any descendant reads the value from the *nearest* provider above it: ```jsx const theme = useContext(ThemeContext); // any React version const theme = use(ThemeContext); // React 19, may sit inside a condition ``` If there is no provider above, the reader gets the default passed to `createContext`. That is the entire feature: a value travels down by tree position instead of through props. Nothing about it is stateful — each render, the provider hands down whatever value it was given this render. ## What a store is A store owns a piece of state and exposes three things: a way to read the current value, a way to change it, and a way to subscribe to changes. Two properties follow, and neither is available from Context: - **The state exists independently of the tree.** It is there before the first component mounts and after the last one unmounts, so non-React code can read and write it. - **Subscription is per-consumer.** A consumer registers interest — usually with a selector function — and is only notified when the part it selected changes. On top of that, store libraries add ecosystem features: devtools with an action timeline, middleware, persistence and rehydration, and testability through a swappable instance. ## The three gaps, stated plainly 1. **Context holds no state.** The value it publishes is produced by something else. If the provider component calls `useState`, `useState` owns the state and Context merely distributes it. 2. **There is no selector.** Every consumer of a context reads the whole published value; the API has no way to express "wake me only when `user.name` changes". The techniques for coping with that live in the re-render discussion, but the capability itself is simply absent. 3. **There is no access from outside a component.** There is no function that hands you a context's current value from a plain module. Only a component rendering under the provider can read it, and only during render. Likewise, the only way to change a context value is to re-render the provider with a new value. ## Where Context is exactly the right tool Dependency injection of things that rarely change and are consumed whole: - theme, locale, and feature flags - the authenticated user object on a page that reloads on login - service handles — an API client, a logger, an analytics instance - swapping an implementation for tests or Storybook, by rendering a different provider - scoping a value to a subtree, where different branches legitimately see different values (a nested provider overrides the outer one) The common thread: the consumer needs the value, not a slice of it, and updates are rare enough that broad re-rendering is not a concern. ## The composition people miss The strongest pattern is not "Context *or* store" but Context carrying the store: ```jsx const StoreContext = createContext(null); // provider publishes the store instance — a stable value that never changes <StoreContext value={store}>{children}</StoreContext> ``` Components pull the instance out of Context (dependency injection, so tests and multiple independent instances work) and then subscribe to it for state (fine-grained updates). The context value never changes, so the injection itself costs nothing. Most store libraries offer exactly this arrangement. ## Answering the interview version When someone asks "can Context replace Redux?", the answer that lands is: for *distributing* values it already replaced the old `connect`-for-prop-drilling use case; for *owning* frequently-changing state read by many components in slices, or state that non-React code touches, it was never the same tool. Name the missing capability — subscription granularity and out-of-tree access — rather than expressing a preference.

  • If Context holds no state, what does the value a consumer sees actually depend on?
    On what the nearest provider above it rendered this pass. The provider component computes the value — typically from its own `useState` or `useReducer` — and passes it to the context. When that owner's state changes, the provider re-renders with a new value and consumers see it. With no provider above, the consumer gets the default argument given to `createContext`.
  • Does putting a store instance into Context cause re-renders when the store's state changes?
    No. The context value is the store object itself, which is a stable reference created once, so the provider publishes the same value forever and context never triggers an update. Re-renders come from components subscribing to the store, each waking only for the slice it selected. That is precisely why the combination is useful.
  • When two providers for the same context are nested, which one does a component read?
    The nearest one above it in the tree. Context lookup walks up from the reading component and stops at the first matching provider, so an inner provider shadows an outer one for its subtree. This is what makes scoped overrides possible — a modal or a preview pane can publish a different theme without affecting the rest of the app.

saying these in an interview costs you the question

  • Context is React's built-in Redux replacement
  • Context stores the state for you
  • Context re-renders only components reading the changed field
  • Using Context means you no longer need useState
  • Any module can read a context value directly

context

open as a page

What concrete signals tell you that a piece of client state has outgrown React Context and belongs in an external store, and which signals mean you should stay with Context?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Move to a store when many consumers each need a different slice of frequently-changing state, when non-React code reads or writes it, or when you need middleware, devtools and persistence. Stay with Context for rarely-changing values consumed whole.

open as a page

In a React 19 app, code outside the component tree — an HTTP client interceptor, a WebSocket message handler, an analytics module — needs to read and update the current auth token. Why can React Context not serve that, and how would you structure it instead?

level: middleimportance: should knowfreq 45%

basics

~20 s

Context values are only readable from a component rendering under the provider, during render, so plain modules cannot reach them. Keep the token in a module-level store that any code can read and write, and expose it to components with useSyncExternalStore.

open as a page

A React codebase has accumulated around thirty Context providers, and a tech lead proposes consolidating client state into a single external store. How would you evaluate that proposal, and what would you measure before committing?

level: principalimportance: should knowfreq 32%

basics

~20 s

Separate the contexts by role first: most are usually dependency injection of values that never change and should stay. Measure which ones actually update at runtime, how often, how many consumers need slices, and how many non-React readers exist — then migrate only those.

open as a page