
React 19: `use` vs `useContext`, what changes and when to pick each
React 19's new use() API overlaps with useContext but breaks the Rules of Hooks on purpose. Here's how they differ, with examples for context and promises.
React 19 ships a new built-in function called use. On the surface it looks like a drop-in replacement for useContext — you can call use(ThemeContext) and get the same value back. But use is not a hook, doesn’t follow the Rules of Hooks, and does a lot more than read context. If you’ve already read my guide to useContext, this is the natural next step: understanding where use fits, and when reaching for it actually buys you something.
use is not a hook
Every hook you know — useState, useEffect, useContext — must be called unconditionally, at the top level of a component, in the same order on every render. That’s the rule that lets React track hook state by call position. use deliberately breaks this rule: you can call it inside an if, a loop, or after an early return.
function Greeting({ showTheme }) {
// Illegal with useContext, perfectly fine with use()
if (showTheme) {
const theme = use(ThemeContext);
return <h1>Theme: {theme}</h1>;
}
return <h1>Hello</h1>;
}
React can allow this because use isn’t tracking hook state across renders the way useState does — it’s reading a value that already exists (a context, or a promise’s resolved value) at the moment it’s called. That’s also why the React team avoided naming it useUse or similar: it’s intentionally exempt from the use* naming convention and its rules.
ℹ️ Note
use still can’t be called inside a try/catch block, and it can’t be called in a regular
JavaScript function that isn’t a component or a custom hook — React needs to know it’s running
inside a render.
Reading context: use(Context) vs useContext(Context)
For the simple case, they’re interchangeable:
// With useContext
function Toolbar() {
const theme = useContext(ThemeContext);
return <div className={theme}>...</div>;
}
// With use
function Toolbar() {
const theme = use(ThemeContext);
return <div className={theme}>...</div>;
}
The practical difference shows up when the context read is conditional. Say a component only needs a context value in one branch:
function Panel({ isExpanded }) {
// With useContext you'd have to call it unconditionally anyway,
// even if you only use the value inside the `if`.
const theme = useContext(ThemeContext);
if (!isExpanded) return null;
return <div className={theme}>Expanded content</div>;
}
function Panel({ isExpanded }) {
if (!isExpanded) return null;
// With use(), you only pay for the context read when it's actually needed
const theme = use(ThemeContext);
return <div className={theme}>Expanded content</div>;
}
Functionally these two versions behave the same in most cases, since reading context is cheap. The real win isn’t performance — it’s being able to write components whose hook-like calls follow the component’s actual logic instead of always running unconditionally at the top.
Reading promises with use() and Suspense
This is where use does something useContext was never designed for: it can unwrap a promise, suspending the component until the promise resolves.
function Comments({ commentsPromise }) {
// `use` suspends this component until the promise resolves
const comments = use(commentsPromise);
return (
<ul>
{comments.map((comment) => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
);
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<p>Loading comments...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
While commentsPromise is pending, React shows the nearest <Suspense> fallback. Once it resolves, Comments renders with the resolved value — no useEffect, no manual loading state, no isPending flag to manage by hand.
⚠ Important
Don’t create the promise inside the component you call use() in — that would create a brand new
promise on every render and trigger an infinite re-render/suspend loop. The promise should come
from outside the render (a Server Component, a cache, or a stable reference created once), and be
passed down as a prop, the same way commentsPromise is passed into Comments above.
If the promise rejects, the nearest error boundary catches it — which pairs naturally with the pattern I covered in my error boundaries article.
use vs useContext at a glance
useContext |
use |
|
|---|---|---|
| Reads context | Yes | Yes |
| Reads promises | No | Yes |
| Can be called conditionally / in loops | No | Yes |
| Follows the Rules of Hooks | Yes | No (exempt on purpose) |
| Available since | React 16.8 | React 19 |
| Callable outside components | No | No (still needs a render context) |
So which one should you use?
- If you’re just reading context unconditionally at the top of a component,
useContextstill works fine and is perfectly idiomatic — you don’t need to rewrite existing code just becauseuseexists. - Reach for
usewhen the context read is genuinely conditional, or when it needs to live inside a loop or after an early return. - Reach for
usewhen you need to unwrap a promise during render — typically data handed down from a Server Component or afetchcall cached outside the component — and want Suspense to handle the loading state for you.
💡 Consiglio
use composes well with the pattern where a Server Component starts a data fetch, passes the
unresolved promise down as a prop, and a Client Component calls use() on it to stream the UI in
as soon as it’s ready — without blocking the initial page render. That pattern is central to React
Server Components, which I cover in a dedicated article.
FAQ
❓ Can I replace all my useContext calls with use?
Yes, syntactically use(Context) and useContext(Context) return the same value. But there’s no
benefit in replacing calls that are already unconditional at the top of a component — do it only
where you actually need the conditional-call flexibility or promise support that use adds.
❓ Does use() replace useEffect + fetch for data fetching in Client Components?
Partially. use() unwraps a promise you already have — it doesn’t start the fetch itself. You
still need something to create that promise (a Server Component, a framework data-loading API, or
a cache). Think of use as the consumption side of async data, not a fetching library.
❓ Is use() a React Hook?
No. React intentionally documents it as an API that “uses” a resource, not a hook — that’s why it
doesn’t follow the Rules of Hooks and isn’t listed alongside useState/useEffect in the hooks
reference.
Conclusion
use and useContext overlap on context reading, but use is the more flexible, more general tool — conditional calls, loops, and promise support — at the cost of being a newer, less battle-tested API. Keep useContext for the straightforward cases you already have; reach for use when your component’s logic genuinely needs the flexibility, or when you’re wiring up Suspense-based data fetching.
References

Co-Founder & CTO at PAPION. Senior full-stack engineer specializing in React, TypeScript, Node.js, and application security.