createContext
Creates a context object so components can provide a value and read it anywhere below without props.
const SomeContext = createContext(defaultValue)- Added in React 16.3.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$defaultValue | any | The value the context has when there is no matching provider in the tree above the component reading it. If you have no meaningful default, pass null. It is a last-resort fallback: it is static and never changes over time. |
Return value
ContextA context object. The object holds no data itself — it represents which context components read or provide. It carries `SomeContext` (render it to provide a value, React 19+), `SomeContext.Provider` (the legacy way to provide a value) and `SomeContext.Consumer` (an alternative, rarely used way to read the value).
Changelog
- PHP 19.0.0`<SomeContext>` can be rendered directly as a provider. `<SomeContext.Provider>` still works but is now the legacy form.
- PHP 16.3.0Introduced as the official context API, replacing the legacy `contextTypes` / `childContextTypes` experiment.
See also
createContext opens a channel: one component provides a value, and any component beneath it reads that value without the value being threaded through every intermediate prop. The object it returns stores nothing — it is only the key that pairs providers with readers.
When to reach for it
Use it for values a whole subtree needs and that change rarely: the current theme, the signed-in user, the locale, or a dispatch function from useReducer. Do not reach for it just to avoid passing a prop two levels down — passing JSX as children usually solves that with far less indirection.
Providing and reading
From React 19 the context is its own provider: <ThemeContext value={theme}>. <ThemeContext.Provider value={theme}> is equivalent and is the only form before 19. Read it with useContext(ThemeContext), or with use(ThemeContext) when the read has to happen inside an if or after an early return.
What trips people up
The default value applies only when no provider sits above the reader. It is not an initial value and it never updates, so a component that renders fine in a test but shows the default in the app is almost always mounted outside the provider. A component rendering a provider does not read from it either — it reads whatever provider is above itself. Every consumer re-renders whenever the provider's value changes by Object.is.
Example
When to use it
- Give an entire page tree the current theme and a setter without passing them through layout components.
- Expose the signed-in user and permission checks to deeply nested menus and buttons.
- Hand a useReducer dispatch function to far-away children so they can send actions without prop chains.
Frequently asked questions
Why is my component reading the default value instead of the provider value?
Because it is not rendered below the provider. The default value is used only when no matching provider exists above the reading component in the tree — check that the component is inside the provider's children, not a sibling of it, and that you are not importing two different context objects from two different files.
Does createContext replace Redux or Zustand?
No. Context is a transport mechanism, not a state manager — it has no store, no selectors and no partial subscriptions, so every consumer re-renders when the provider value changes. It is a good fit for low-frequency values like theme or user, and a poor fit for high-frequency state that only a few components care about.
Do I still need Context.Provider in React 19?
No, in React 19 you render the context itself: <ThemeContext value={theme}>. <ThemeContext.Provider> still works and remains the correct form on React 18 and earlier; React plans to deprecate the .Provider spelling in a future version.
More examples
Value and updater in one context, memoized
The pane shows a "Sign in" button; clicking it swaps to "Signed in as Ada" with a sign-out button. The custom useAuth hook turns the null default into a clear error message instead of a silent undefined read, and useMemo keeps the context value referentially stable between renders where user has not changed.
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(null);
// Without useMemo this object is new on every render of AuthProvider,
// so every consumer re-renders even when `user` is unchanged.
const value = useMemo(
() => ({
user,
signIn: name => setUser({ name }),
signOut: () => setUser(null)
}),
[user]
);
return <AuthContext value={value}>{children}</AuthContext>;
}
function Toolbar() {
const { user, signIn, signOut } = useAuth();
return user ? (
<p>
Signed in as {user.name} <button onClick={signOut}>Sign out</button>
</p>
) : (
<button onClick={() => signIn("Ada")}>Sign in</button>
);
}
export default function App() {
return (
<AuthProvider>
<Toolbar />
</AuthProvider>
);
}Nested providers override, and the legacy Provider/Consumer form
Output reads: "depth 0 depth 1 depth 2 legacy read: 2". The first Depth has no provider above it so it falls back to the default 0. Each nested provider shadows the one above for its own subtree, and <LevelContext value={1}> and <LevelContext.Provider value={2}> behave identically — the second is only the older spelling.
import { createContext, useContext } from "react";
const LevelContext = createContext(0);
function Depth() {
const level = useContext(LevelContext);
return <span>depth {level} </span>;
}
// Pre-React-19 form. Still valid, just more typing.
function LegacyDepth() {
return (
<LevelContext.Consumer>
{level => <span>legacy read: {level}</span>}
</LevelContext.Consumer>
);
}
export default function App() {
return (
<div>
<Depth />
<LevelContext value={1}>
<Depth />
<LevelContext.Provider value={2}>
<Depth />
<LegacyDepth />
</LevelContext.Provider>
</LevelContext>
</div>
);
}
Discussion