The use() hook, Suspense, and async data in React 19
React 19’s use API reads resources—currently context and promises—during render. When a promise is not yet resolved, React suspends the nearest <Suspense> boundary instead of forcing you into useEffect + useState boilerplate.
Reading a promise
'use client';
import { use, Suspense } from 'react';
function UserName({ userPromise }: { userPromise: Promise<{ name: string }> }) {
const user = use(userPromise);
return <span>{user.name}</span>;
}
export function Profile({ userPromise }: { userPromise: Promise<{ name: string }> }) {
return (
<Suspense fallback={<span>Loading…</span>}>
<UserName userPromise={userPromise} />
</Suspense>
);
}
The promise should be stable per request (create in Server Component and pass down, or from a cache layer)—do not create a fresh promise on every parent render without memoization or you will loop.
Comparison to useEffect
useEffect runs after paint; use suspends before commit of that subtree. That improves perceived loading when paired with meaningful fallbacks.
Next.js interaction
Server Components can await or pass promises to client boundaries. Verify your Next/React versions support the pattern you ship; frameworks evolve quickly.
Summary
use + Suspense is a declarative async story on the client. Treat promises as first-class props, respect Suspense boundaries, and avoid recreating promises unintentionally.