React 19 lets you render a context directly instead of Context.Provider, plus the Context.use pattern and how to migrate.

Bishal Biswas
Self Learner
The small change
Before React 19:
const ThemeContext = createContext(null);
function App() {
return (
<ThemeContext.Provider value={theme}>
<Page />
</ThemeContext.Provider>
);
}
From React 19:
function App() {
return (
<ThemeContext value={theme}>
<Page />
</ThemeContext>
);
}
Render the context itself. .Provider is gone.
Why it changed
Context.Provider was always slightly odd. A context has exactly one provider component, so requiring you to reach into a property added noise without adding meaning.
The old syntax still works and is not removed yet, but it is deprecated. New code should use the short form.
Nesting is much tidier
The improvement shows up most where providers stack.
function App() {
return (
<AuthContext.Provider value={auth}>
<ThemeContext.Provider value={theme}>
<LocaleContext.Provider value={locale}>
<CartContext.Provider value={cart}>
<Router />
</CartContext.Provider>
</LocaleContext.Provider>
</ThemeContext.Provider>
</AuthContext.Provider>
);
}
function App() {
return (
<AuthContext value={auth}>
<ThemeContext value={theme}>
<LocaleContext value={locale}>
<CartContext value={cart}>
<Router />
</CartContext>
</LocaleContext>
</ThemeContext>
</AuthContext>
);
}
Same structure, considerably less noise.
Consumer is gone too
The old render prop API for reading context in class components:
<ThemeContext.Consumer>
{(theme) => <button className={theme}>Click</button>}
</ThemeContext.Consumer>
This is removed in React 19. Use useContext, which has been the recommended way since hooks arrived.
Reading with use
React 19 also lets you read a context with the new use API.
import { use } from 'react';
function Button() {
const theme = use(ThemeContext);
return <button className={theme}>Click</button>;
}
At first glance this is just another name for useContext. The difference is that use is not bound by the rules of hooks, so it can be called conditionally.
function Message({ showTheme }) {
if (showTheme) {
const theme = useContext(ThemeContext);
}
return null;
}
function MessageWithUse({ showTheme }) {
if (showTheme) {
const theme = use(ThemeContext);
return <p className={theme}>Themed</p>;
}
return <p>Plain</p>;
}
That is genuinely useful when a component only sometimes needs a context, or needs it inside a loop.
use does more than contexts. It also reads promises, and that gets its own topic later.
Combining providers
Stacking providers still gets deep. A small helper flattens it.
function Providers({ children }) {
return (
<AuthProvider>
<ThemeProvider>
<LocaleProvider>
<CartProvider>{children}</CartProvider>
</LocaleProvider>
</ThemeProvider>
</AuthProvider>
);
}
function App() {
return (
<Providers>
<Router />
</Providers>
);
}
Or compose them programmatically:
function composeProviders(...providers) {
return function Composed({ children }) {
return providers.reduceRight(
(acc, Provider) => <Provider>{acc}</Provider>,
children
);
};
}
const Providers = composeProviders(
AuthProvider,
ThemeProvider,
LocaleProvider,
CartProvider
);
Neat, though the explicit version is usually easier to read and debug. Use judgement.
Migrating
The change is mechanical:
- Replace
<X.Provider value={v}>with<X value={v}>. - Replace every
<X.Consumer>withuseContext(X)oruse(X). - Closing tags become
</X>.
A codemod exists for the first two:
npx codemod@latest react/19/replace-context-provider
There is no rush. The old syntax keeps working, so migrate as you touch files.
The rest is unchanged
Everything from the previous topic still applies:
- Every consumer re-renders when the value changes.
- Split contexts by concern.
- Memoise the value object, unless the compiler handles it.
- Wrap access in a custom hook that throws when the provider is missing.
The syntax got shorter. The performance characteristics did not change at all.
Quick recap
- React 19 renders the context itself as the provider.
<X.Provider>still works but is deprecated.Context.Consumeris removed, so useuseContextoruse.use(Context)can be called conditionally, unlikeuseContext.- Group providers in one component to keep
Appreadable. - A codemod handles the mechanical parts of migrating.
- None of the performance advice changes.
import { createContext, useContext, use, useState, useMemo } from 'react';
const ThemeContext = createContext(null);
const AuthContext = createContext(null);
// REACT 19: render the context itself. No .Provider.
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const value = useMemo(
() => ({ theme, toggle: () => setTheme((t) => (t === 'light' ? 'dark' : 'light')) }),
[theme]
);
return <ThemeContext value={value}>{children}</ThemeContext>;
}
// The old form, deprecated but still working:
// <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
// Reading with useContext, which follows the rules of hooks
function Header() {
const { theme, toggle } = useContext(ThemeContext);
return <button onClick={toggle}>Theme: {theme}</button>;
}
// Reading with use(), which does NOT, so it can go inside a condition
function Message({ showTheme }) {
if (showTheme) {
const { theme } = use(ThemeContext); // legal, unlike useContext
return <p className={theme}>Themed message</p>;
}
return <p>Plain message</p>;
}
// Context.Consumer is REMOVED in React 19. This no longer works:
// <ThemeContext.Consumer>{(theme) => <b>{theme}</b>}</ThemeContext.Consumer>
// Nesting is much tidier without .Provider everywhere
function Providers({ children }) {
return (
<AuthProvider>
<ThemeProvider>{children}</ThemeProvider>
</AuthProvider>
);
}
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({ user, setUser }), [user]);
return <AuthContext value={value}>{children}</AuthContext>;
}
export default function App() {
return (
<Providers>
<Header />
<Message showTheme />
</Providers>
);
}
// Migrating is mechanical, and there is a codemod for it:
// npx codemod@latest react/19/replace-context-provider
// Everything about performance is unchanged: split contexts by concern,
// memoise the value, and every consumer still re-renders on a change.
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready