skip to content

Context API

React's built-in way to hand a value to a deep subtree without threading props through every level — and the re-render cost that comes with it. Interviewers ask about context constantly because most candidates treat it as a state manager and get bitten.

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

explore

questions

13

When a React context provider is rendered with a new value, which components re-render because of that context change — every descendant of the provider, or only the ones that read the context?

level: juniorimportance: must knowfreq 62%

answer

  1. readers, not descendants
  2. two separate causes, same symptom
  3. memo compares props only
  4. depth does not matter for propagation
  5. Object.is on the value you passed

basics

~20 s

Only components that read that context re-render because of the change. React notifies the consumers it finds below the provider. Other descendants re-render for a different reason: their own parent re-rendered and re-created their elements.

solid answer

~40 s

A context change propagates only to consumers — components that call `useContext(MyContext)` or `use(MyContext)`. React compares the old and new value with `Object.is`; if they differ, it schedules work on every consumer beneath that provider, no matter how deep. Non-consuming descendants are untouched by that mechanism. What confuses people is that in practice the whole subtree often does re-render — but that is the ordinary cause: the component holding the state re-rendered, so it re-created its children's elements. Two separate causes with the same symptom. It also means `React.memo` does not protect a consumer: memo compares props, and a context read is not a prop, so a memoized consumer still re-renders when the value changes.

go deeper

for a junior

Be able to say plainly that a context update re-renders the components that read the context, at any depth, and that React decides by comparing the value it was given.

for a middle

Explain the two independent causes — provider value identity versus the owning component re-rendering — and why React.memo blocks the second but not the first.

for a senior

Show how you diagnose which cause is firing in a real app before changing anything, and pick the matching fix rather than sprinkling memo across the subtree.

for a principal

Frame it as an architecture question: where state lives determines how much of the tree is coupled to it, and a provider high in the tree is a decision about blast radius, not a rendering detail.

## The two independent reasons a component under a provider re-renders When you look at a React app and see "everything under the provider re-rendered", there are two different mechanisms that can produce that, and interviewers ask this question to see whether you can tell them apart. **Cause 1 — normal parent-to-child rendering.** The component that owns the state and renders the provider re-rendered. Rendering a component re-runs its function body, which re-creates the JSX elements it returns, which means React renders its children too (unless something bails out). This has nothing to do with context; it would happen with plain props. **Cause 2 — context propagation.** The provider was given a value that is not `Object.is`-equal to the previous one. React then walks the tree below that provider and schedules an update on each fiber that reads this particular context. Only readers are affected — a `<div>`, a layout wrapper, or any component that never calls `useContext` for that context is not a target of this mechanism. A reader is a component that calls `useContext(MyContext)` or, in React 19, `use(MyContext)`. The legacy `MyContext.Consumer` render-prop form and a class component's `contextType` also count as readers, though you rarely write them today. ## Why depth does not matter Context propagation is not parent-to-child hand-off. React does not need each intermediate component to re-render for a deeply nested consumer to receive the new value. That is the whole point of context: a consumer twenty levels down gets the update directly, even if every component in between bails out of rendering. This is also why `React.memo` is not a shield. `React.memo` makes a component skip re-rendering when its props are shallowly equal to last time. A context read is not a prop, so React marks the memoized component as needing work anyway: ```jsx const Badge = React.memo(function Badge() { const { user } = useContext(AuthContext); // still re-renders on value change return <span>{user.name}</span>; }); ``` What `React.memo` *can* do is stop **cause 1** from travelling through it. If a memoized component takes stable props and does *not* read the context, it bails out, and its own children are not re-rendered by the parent's render — even though a consumer further down still gets its context update through cause 2. ## Why the distinction is practical, not academic The fix you reach for depends on which cause you are looking at. If consumers re-render because the **value identity** keeps changing (a fresh object literal on every parent render), the fix lives at the provider: memoize the value, or split it up. If the subtree re-renders because the **provider's owner** re-renders, memoizing the value changes nothing about that subtree — you address it by not re-creating those children, for example by having the provider component render `children` it received as a prop rather than JSX it constructs itself. A quick way to check which one you are seeing: comment out the `useContext` call in a component that is re-rendering. If it still re-renders, you are looking at cause 1 and the provider value is not your problem. ## What "the value changed" means React's comparison is `Object.is` on the value you passed. Two structurally identical objects are different values: ```jsx // new object every render — every consumer re-renders every time <ThemeContext value={{ theme, toggle }}>{children}</ThemeContext> ``` A primitive value behaves the way people intuitively expect: passing the string `"dark"` twice in a row is the same value, so consumers are not notified. It is object and function values — the common case, because a context usually carries state plus updaters — that make this a pitfall. ## In React 19 You can render the context object directly as the provider, `<ThemeContext value={...}>`; `<ThemeContext.Provider value={...}>` still works and behaves identically. Reading with `use(ThemeContext)` is allowed inside conditionals and loops, unlike `useContext`. None of that changes the propagation model described above. ## Saying it in an interview "Context updates target readers, not descendants. React compares the value with `Object.is` and schedules work on every component that reads that context below the provider, at any depth, and `React.memo` doesn't stop it because a context read isn't a prop. If non-readers are also re-rendering, that's the ordinary parent-re-render path, which is a separate thing to fix."

  • If React.memo cannot stop a consumer from re-rendering, when is it still worth putting on a component inside a provider's subtree?
    It still blocks the ordinary parent-render path. A memoized component with stable props that does *not* read the context will bail out, so it and its non-consuming children skip rendering when the provider's owner re-renders. It just cannot block the context mechanism for a component that actually reads the context.
  • How would you tell, in a real app, whether a component is re-rendering because of the context or because its parent re-rendered?
    Remove or comment out the `useContext` call and see if it still re-renders — if it does, it is the parent path. The React DevTools Profiler is the non-invasive version: it labels why each component rendered, distinguishing a parent render from a context change.
  • Does an intermediate component between the provider and a consumer have to re-render for the consumer to get the new value?
    No. React schedules work directly on the consuming fibers, so the update reaches a deeply nested consumer even if every component in between bails out. That direct delivery is exactly what context exists to provide.

saying these in an interview costs you the question

  • Says every descendant of the provider re-renders
  • Thinks React.memo prevents context-driven re-renders
  • Believes the value has to be handed down through each level
  • Assumes context does a deep comparison of the value
  • Confuses a parent re-render with a context update

context

open as a page

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%

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.

open as a page

Instead of exporting an AuthContext for callers to pass to useContext, many React codebases export only a custom useAuth() hook that throws when it finds no value. What does that pattern buy you, and how do you implement it?

level: middleimportance: must knowfreq 70%

basics

~20 s

The hook turns "you forgot the Provider" into an immediate, named error at the misuse site instead of a null that spreads through the app. Create the context with null, read it in the hook, throw if it is null, and export only the hook and the Provider.

open as a page

A React provider is written as `<AuthContext value={{ user, login, logout }}>{children}</AuthContext>` inside a component that re-renders often. Why does every consumer re-render even when user has not changed, and how do you fix it?

level: middleimportance: must knowfreq 78%

basics

~20 s

The object literal creates a new object on every render, so React sees a new context value each time and notifies all consumers. Wrap the value in useMemo keyed on its real dependencies, and keep the functions stable too.

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 React, what is the argument you pass to createContext(defaultValue) used for, and in which situation does a component actually receive that default?

level: juniorimportance: should knowfreq 58%

basics

~20 s

createContext's argument is the value a consumer receives when no matching Provider sits above it in the tree. If a Provider is present but passes undefined, the consumer gets undefined — the default is not a general fallback.

open as a page

A React tree renders <ThemeContext value="dark"> around the page and, deeper inside it, <ThemeContext value="light"> around a modal. What does a component inside the modal read, and what is nesting the same context deliberately good for?

level: middleimportance: should knowfreq 46%

basics

~20 s

It reads "light": React returns the nearest matching Provider above the reader, and values do not merge. Deliberate nesting scopes a value to a subtree — a local override, or a fresh independent instance of a Provider that owns state.

open as a page

In a React app where one context provides both the state and the function that updates it, why do teams split it into two separate contexts — one for the state and one for the updater — and what does that buy?

level: middleimportance: should knowfreq 52%

basics

~20 s

Components that only trigger updates do not need the state. Putting the updater in its own context gives it a value that never changes, so those write-only components stop re-rendering every time the state changes.

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 app's root has grown into eight nested context providers wrapping <App />. How do you tame that pyramid, and what does the nesting order actually mean?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Nesting order is real dependency order: a provider can only read contexts from providers above it. Tame the pyramid by moving providers down to the subtree that needs them, collecting the rest into one AppProviders component, and folding an ordered array with reduceRight where order is free.

open as a page

A React context holds an object with several unrelated fields, and a component that reads only one of them re-renders whenever any field changes. Why can't the component subscribe to just that field, and what do you do about it?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Context has no selector API: React compares the whole value with Object.is and notifies every reader, with no way to opt out per field. The structural fix is to split the value into several contexts along how often each part changes.

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

You own a shared React module that other teams consume — say a <FeatureFlagProvider> plus hooks. What do you decide about its public surface: what to export, whether the Provider owns its own state, and whether two instances may coexist?

level: principalimportance: nice to knowfreq 24%

basics

~20 s

Export the Provider and hooks, keep the context object private, and decide deliberately whether the Provider owns state or accepts it as a prop. Then state explicitly whether two mounted instances mean two independent scopes or a misconfiguration you should detect.

open as a page