Diego Betto's Blog
React 19 - use vs useContext

Diego Betto · September 10, 2026 · 6 min di lettura

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.

Condividi:XLinkedInFacebookWhatsApp

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, useContext still works fine and is perfectly idiomatic — you don’t need to rewrite existing code just because use exists.
  • Reach for use when the context read is genuinely conditional, or when it needs to live inside a loop or after an early return.
  • Reach for use when you need to unwrap a promise during render — typically data handed down from a Server Component or a fetch call 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

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Written by

Diego Betto

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