useContext
Reads the value of a React context from the nearest provider above the component.
const value = useContext(SomeContext)- Added in React 16.8.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$SomeContext | Context | The context object previously created with createContext. Pass the context itself, not its .Provider or .Consumer. The context holds no data — it only identifies the kind of value being provided and read. |
Return value
anyThe context value for the calling component: the `value` passed to the closest matching provider above it in the tree, or the `defaultValue` given to `createContext` when there is no provider at all. The value is always up to date; React re-renders context readers when it changes.
Errors
React throws an "Invalid hook call" error if you call it outside a component body or another Hook, and warns if you pass SomeContext.Provider or SomeContext.Consumer instead of the context object itself.
Changelog
- PHP 19.0.0You can render <SomeContext value={...}> directly as a provider; <SomeContext.Provider> still works but is no longer required.
- PHP 19.0.0The new `use` API can read the same context and, unlike useContext, may be called inside conditions and loops.
- PHP 18.0.0Context updates are batched with other state updates in automatic batching, so a provider value changed in a promise or timeout now produces a single re-render pass.
useContext subscribes a component to a context and returns whatever the nearest provider above it supplies. React re-renders every reader whose context value changes, comparing the previous and next value with Object.is.
When to reach for it
Use it when a value is needed by many components at different depths and threading props through the middle is pure noise — the current theme, the signed-in user, a locale, a design-system registry. Do not reach for it as a general state manager: it is a transport mechanism, not a store. Frequently changing values placed in context re-render every reader in the subtree.
What trips people up
- The provider must be above you. A provider rendered by the same component that calls
useContextdoes not affect that call. Move the provider to the parent, or split the component in two. defaultValueis a fallback for a missing provider, not a missing value. If a provider exists and passesvalue={undefined}, you getundefined.memodoes not block context. Memoizing a child stops prop-driven re-renders, not context-driven ones.- Object identity matters. If your bundler produces two copies of the module that calls
createContext, the provider and the reader hold different objects and the lookup silently falls through to the default.
Passing an inline object as value creates a new identity on every render; wrap it in useMemo when the provider re-renders often.
Example
When to use it
- Read the active theme deep inside a component tree without passing a theme prop through every layer.
- Expose the signed-in user and their permissions to any screen that needs to gate UI.
- Let form fields find the form-level state and validation registry their parent created.
- Share a locale and formatter set with every component that renders dates or currency.
Frequently asked questions
Why is useContext returning undefined instead of my default value?
Because a provider is present but is passing undefined. The defaultValue you gave createContext is used only when there is no matching provider anywhere above the component; once a provider exists, its value prop wins even if that value is undefined. Check that the provider actually sets value={...}, and that you are not rendering the provider inside the same component that reads the context.
Is useContext slow, and does it re-render everything?
The read itself is cheap, but every component reading a context re-renders when that context's value changes by Object.is, and memo will not stop it. The usual cost comes from putting an inline object literal in value, which creates a new identity every render. Wrap the value in useMemo, and split unrelated concerns (data vs. dispatch) into separate contexts so readers only subscribe to what they use.
What is the difference between useContext and use for reading context?
use(SomeContext) reads the same value but may be called conditionally, inside loops, or after an early return, whereas useContext must run unconditionally at the top level like every Hook. use was added in React 19; in existing code useContext remains the standard way to read context and there is no performance difference between them.
More examples
Pairing context with a reducer
Two contexts are provided: one for the data and one for the dispatch function. Splitting them means components that only dispatch are not re-rendered when the task list changes. The output is a two-item checklist where 'Read the docs' starts checked; toggling a box dispatches through context, the reducer produces a new array, and TaskApp re-renders along with TaskList, since the reducer state lives in TaskApp. Components that read only TasksDispatchContext are the ones spared, because dispatch keeps a stable identity across renders.
import { createContext, useContext, useReducer } from "react";
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
function tasksReducer(tasks, action) {
switch (action.type) {
case "added":
return [...tasks, { id: action.id, text: action.text, done: false }];
case "toggled":
return tasks.map(t => (t.id === action.id ? { ...t, done: !t.done } : t));
default:
throw new Error("Unknown action: " + action.type);
}
}
function TaskList() {
const tasks = useContext(TasksContext);
const dispatch = useContext(TasksDispatchContext);
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
<label>
<input
type="checkbox"
checked={task.done}
onChange={() => dispatch({ type: "toggled", id: task.id })}
/>
{task.text}
</label>
</li>
))}
</ul>
);
}
export default function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, [
{ id: 1, text: "Read the docs", done: true },
{ id: 2, text: "Ship the feature", done: false }
]);
return (
<TasksContext value={tasks}>
<TasksDispatchContext value={dispatch}>
<h3>Tasks</h3>
<TaskList />
</TasksDispatchContext>
</TasksContext>
);
}A custom hook that enforces the provider
The default value is null so that reading the context outside the provider is a loud error instead of a silent undefined. The output reads 'Signed in as Ada (admin)'; clicking Sign out sets user to null, which produces a new memoized value and re-renders Greeting to 'Signed out.'
import { createContext, useContext, useMemo, useState } from "react";
const AuthContext = createContext(null);
function useAuth() {
const auth = useContext(AuthContext);
if (auth === null) {
throw new Error("useAuth must be used inside <AuthProvider>");
}
return auth;
}
function AuthProvider({ children }) {
const [user, setUser] = useState({ name: "Ada", role: "admin" });
// Without useMemo, a new object identity on every render would
// re-render every consumer even when nothing changed.
const value = useMemo(
() => ({ user, signOut: () => setUser(null) }),
[user]
);
return <AuthContext value={value}>{children}</AuthContext>;
}
function Greeting() {
const { user, signOut } = useAuth();
if (!user) return <p>Signed out.</p>;
return (
<p>
Signed in as {user.name} ({user.role}){" "}
<button onClick={signOut}>Sign out</button>
</p>
);
}
export default function Account() {
return (
<AuthProvider>
<Greeting />
</AuthProvider>
);
}Overriding a value for one subtree
Each Section reads the level provided above it and provides one more to its children, so nesting alone decides heading depth. The rendered output is an h2, then an h3, then an h4. Note that Section's own useContext call is unaffected by the provider Section returns — that provider only applies to children.
import { createContext, useContext } from "react";
const LevelContext = createContext(1);
function Heading({ children }) {
const level = useContext(LevelContext);
const Tag = "h" + Math.min(level, 6);
return <Tag>{children}</Tag>;
}
function Section({ children }) {
const level = useContext(LevelContext);
// Reads the level from above, provides level + 1 to everything below.
return <LevelContext value={level + 1}>{children}</LevelContext>;
}
export default function Document() {
return (
<Section>
<Heading>Top level</Heading>
<Section>
<Heading>Nested once</Heading>
<Section>
<Heading>Nested twice</Heading>
</Section>
</Section>
</Section>
);
}
Discussion