A React component both calls useContext(ThemeContext) and renders <ThemeContext value="dark"> around its own children. Which value does that component's own useContext call receive, and how does React resolve the lookup?
answer
- direction matters
- hooks run before the return value mounts
- provider is below the component that renders it
- nearest match, then stop walking
- shadowing, not merging
basics
~20 sThe component reads from the nearest provider above itself, or the context default if there is none. A provider it renders sits below it in the tree, so it affects the children only, never the component that renders it.
solid answer
~50 sIt does not see `"dark"`. `useContext` resolves by walking **upward** from the component that calls it, looking for the nearest ancestor provider created from the same context object; the provider this component renders is part of its output, so it is a descendant position, not an ancestor. The component therefore gets whatever provider is above it, or the `createContext` default if none exists. The same rule explains nested providers: with an outer `"light"` and an inner `"dark"`, a consumer inside the inner one reads `"dark"` because nearest wins, and a consumer between them reads `"light"`. Nothing merges — each consumer resolves independently by its own position in the tree. If you need the component itself to use the value it provides, hoist the provider into a parent component or compute the value once and pass it down explicitly.
code
jsx · 23 linesimport { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function Body() {
return <span>body sees: {useContext(ThemeContext)}</span>;
}
function Panel() {
const own = useContext(ThemeContext); // 'light' - resolved above Panel
return (
<div>
<span>panel sees: {own}</span>
<ThemeContext value="dark">
<Body />
</ThemeContext>
</div>
);
}
export default function App() {
return <Panel />;
}go deeper
Remember the direction: a component reads context from providers wrapped around it, not from a provider it renders itself. Say that children get the provided value and the provider component does not.
Explain why: hooks run during the component's own body, and its returned provider becomes a descendant. Describe the upward walk to the nearest matching provider, and that nesting shadows rather than merges.
Turn it into design guidance — split the providing component from the consuming one so the tree matches the intent, and be ready to explain why portaled children still see providers above their React parent.
Own the boundary question: how deep providers should be nested and where subtree overrides are legitimate versus a sign that the value should have been passed explicitly or split into separate contexts.
## The rule in one sentence `useContext(SomeContext)` returns the `value` of the nearest provider for that same context object **strictly above** the calling component in the rendered tree, or the context's default if there is none. Everything about context reads follows from "nearest" plus "above". ## Why a component cannot read the provider it renders Hooks run while the component's own function body executes. What that function *returns* — including a provider element — has not been mounted yet, and when it is, it lands **below** the component in the tree. So a provider in a component's own JSX is an ancestor of its children and a descendant of the component itself. ```jsx function Panel() { const theme = useContext(ThemeContext); // reads from ABOVE Panel return ( <ThemeContext value="dark"> <Body /> {/* Body reads "dark" */} </ThemeContext> ); } ``` `theme` here is whatever surrounds `<Panel />`, or the default. `Body` reads `"dark"`. This trips people up because the provider is visually inside the same file, and file position feels like scope. Context is not lexical scope — it is tree position at render time. A component rendered through `props.children` is positioned where it was *written*, not where it is *rendered into*, which is the other half of the same idea: the provider that matters is the one that is an ancestor in the element tree the parent built. ## Nested providers: nearest wins, nothing merges ```jsx <ThemeContext value="light"> <A /> {/* "light" */} <ThemeContext value="dark"> <B /> {/* "dark" */} </ThemeContext> </ThemeContext> ``` Each consumer resolves independently by walking up until it hits the first matching provider. There is no merge, no shallow-combine, no array of values — the inner provider fully shadows the outer one for its subtree, exactly like variable shadowing, and the outer value is untouched everywhere else. That shadowing is a feature: a modal or a preview pane can override a context for its subtree without any coordination with the root provider. ## Matching is by context object identity "Same context object" means the exact object returned by one `createContext` call — not the same name, not the same shape. Two `createContext` calls produce two independent lookups that never see each other's providers. This matters when the same conceptual value is provided by two different context objects: a consumer only reads the one it was handed. ## Fixing the "I need my own value" case When a component genuinely needs the value it is also providing, the shape of the problem is that one component is doing two jobs. Two fixes: 1. **Split the component.** A `ThemeProvider` that only renders the provider, and a `Panel` rendered *inside* it that consumes. Now the tree relationship matches the intent. 2. **Skip context for yourself.** You already have the value in the provider component — it is a local variable. Use it directly and let context serve only the descendants that cannot receive it as a prop. Both are better than the thing people reach for, which is a second `useContext` call or a ref dance; neither changes the direction of the lookup. ## Portals do not change the answer A component rendered through `createPortal` appears elsewhere in the DOM but stays in the same position in the React tree, so it keeps reading the providers above its React parent — not the providers around the DOM node it was portaled into. If context suddenly "works" or "stops working" for portaled content, this is the rule to check. ## What to say in an interview Lead with the direction of the walk: upward from the consumer, nearest matching provider wins, default if the walk finds nothing. Then apply it to the trap in the question — a provider in a component's own return value is below that component, so it serves the children and not the component itself — and mention that nesting shadows rather than merges.
- Two providers for the same context are nested with values "light" and "dark". What does a consumer between them read, and what does one inside the inner provider read?A consumer between them reads `"light"` — its upward walk hits the outer provider first. A consumer inside the inner one reads `"dark"`. Each resolves independently, and the inner provider shadows the outer for its subtree only. Nothing merges, and the outer value is unchanged for everything outside the inner provider.
- A component is rendered through createPortal into a DOM node outside the provider's DOM subtree. Does it still see the context?Yes. A portal changes only where the DOM nodes land; the component keeps its position in the React element tree, so it still reads the providers above its React parent. The providers around the destination DOM node are irrelevant — they are not its React ancestors.
- How would you restructure a component that both provides a context value and needs to read it?Split it. Move the provider into a thin wrapper component and render the consuming component inside that wrapper, so the tree relationship matches the intent. Often you do not even need the read: the providing component already holds the value as a local variable and can use it directly, leaving context for descendants that cannot get it as a prop.
saying these in an interview costs you the question
- Thinks a component reads the provider written in its own JSX
- Treats context as lexical scope rather than tree position
- Says nested providers merge their values
- Believes the outermost provider wins over a nearer one
- Assumes a portal detaches a component from providers above it