David John Thammineni
React24 Apr 20252 min read

React 19's use() Hook: Suspense-First Data in Client Components

React 19's use() Hook: Suspense-First Data in Client Components

React 19 is stable, and use() is the piece reshaping client-side data flow. It reads a promise during render, suspending until it settles — and unlike hooks, you can call it conditionally. The patterns worth adopting:

Stream from server, resolve in client

The headline pattern: a Server Component starts a fetch and passes the pending promise down; a Client Component use()s it:

// Server Component — don't await!
export default function Page() {
  const reviewsPromise = getReviews(productId); // starts immediately
  return (
    <>
      <ProductHeader />                          {/* renders now */}
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews reviewsPromise={reviewsPromise} />
      </Suspense>
    </>
  );
}
"use client";
export function Reviews({ reviewsPromise }) {
  const reviews = use(reviewsPromise);  // suspends until resolved
  const [filter, setFilter] = useState("all"); // full interactivity
  /* ... */
}

The page shell ships immediately; reviews stream in when ready; the client component keeps its state and handlers. This replaces both the useEffect fetch (waterfall, spinner management) and awaiting in the server component (blocks the whole page).

Conditional reads are legal

function Details({ detailsPromise, expanded }) {
  if (!expanded) return <Summary />;
  const details = use(detailsPromise); // fine inside a condition!
  return <FullDetails data={details} />;
}

Because use() isn't a hook, the rules-of-hooks restrictions don't apply. Data that's only needed sometimes is only awaited sometimes.

The pitfall: promise identity

use() resubscribes per promise instance. Create the promise in render and every render creates a new fetch — an infinite suspend loop:

const data = use(fetchThing(id));        // BUG: new promise every render

Promises must come from outside the render cycle: a Server Component prop, a cache, or a memoized source. In practice this means use() complements rather than replaces the data libraries — TanStack Query's caching plus a promise option feeding use() is emerging as the clean combination: library owns identity and invalidation, React owns coordination and streaming.

Keep Reading