The use() API
Read promises and context conditionally with React 19's new use() function
React 19 introduced use() — a function that reads the value of a resource like a Promise or context. Unlike hooks, use() can be called conditionally and inside loops, which unlocks patterns the old rules forbade.
Reading a Promise
use() unwraps a promise. When the promise is pending, the component suspends and the nearest <Suspense> boundary shows its fallback. When it resolves, the component renders with the value.
import { use, Suspense } from "react"
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise) // suspends until resolved
return <h1>{user.name}</h1>
}
function Page() {
const userPromise = fetchUser() // created outside, passed in
return (
<Suspense fallback={<p>Loading…</p>}>
<UserProfile userPromise={userPromise} />
</Suspense>
)
}
No useEffect, no useState, no manual loading flag — use() plus Suspense handles the pending state declaratively.
Creating fetchUser() in the component body means a new promise on every
render — an infinite loop. Create it in a Server Component, a cache, or an
event handler, and pass it down. (Or use a framework's data layer.)
use() Breaks the Hook Rules — On Purpose
The Rules of Hooks say: no calling hooks conditionally or in loops. use() is not a hook, so those restrictions don't apply. This is the whole point.
function Note({ show, notePromise }: Props) {
if (!show) return null
// ✅ Legal — use() can live after an early return or inside an if.
const note = use(notePromise)
return <p>{note.body}</p>
}
Try that with useState and React throws. use() was designed to be callable exactly where hooks can't be.
Reading Context with use()
use() also reads context — and because it's conditional-friendly, you can read context only when you actually need it:
function Item({ premium }: { premium: boolean }) {
if (premium) {
const theme = use(ThemeContext) // only read when premium
return <span style={{ color: theme.accent }}>★</span>
}
return <span>·</span>
}
With useContext, you'd have to call it unconditionally at the top even if you rarely use the value.
The most common pattern: a Server Component starts a fetch (without awaiting
it) and passes the promise to a Client Component that reads it with use().
Data starts loading early; the client unwraps it with a clean Suspense
fallback.