Skip to content

Suspense without waterfalls: where to put the boundaries

Suspense can stream a page piece by piece, but a misplaced boundary turns it into a queue at the post office. Three patterns that truly parallelize loading.

Author
Jan Jarčík
Published
Reading time
2 min read
ShareXLinkedInBluesky

<Suspense> tells React: "while something inside is loading, show the fallback". Sounds simple, but where the boundary sits and when loading starts decide whether data is fetched in parallel or one request after another.

A waterfall in practice

TSX
async function Profile({ id }: { id: string }) {
  const user = await getUser(id);         // 300 ms
  return (
    <>
      <h1>{user.name}</h1>
      <Posts userId={id} />               {/* starts only after 300 ms */}
    </>
  );
}

Posts starts rendering — and therefore fetching — only after Profile finishes its await. Two sequential waits = a waterfall.

Pattern 1: independent boundaries for independent data

TSX
export default function Page({ id }: { id: string }) {
  return (
    <>
      <Suspense fallback={<HeaderSkeleton />}>
        <ProfileHeader id={id} />
      </Suspense>
      <Suspense fallback={<PostsSkeleton />}>
        <Posts userId={id} />
      </Suspense>
    </>
  );
}

Both components are siblings, so they start loading at the same time, and each appears as soon as it's ready.

Pattern 2: start fetching high, wait low

TSX
export default function Page({ id }: { id: string }) {
  const userPromise = getUser(id);   // starts now
  const postsPromise = getPosts(id); // starts now
  return (
    <Suspense fallback={<Skeleton />}>
      <Profile userPromise={userPromise} postsPromise={postsPromise} />
    </Suspense>
  );
}

Inside, you await (Server Component) or use() (Client Component). The requests run in parallel even though they're read deep in the tree.

Pattern 3: group what belongs together

Not every component needs its own boundary. Five skeletons popping in random order feel worse than one that swaps out at once. Put a boundary where a visual unit makes sense — a card, a panel, a section.

Bonus: what changed in React 19

When a component suspends, React 19 commits the nearest fallback immediately and renders the siblings inside the same boundary afterwards ("pre-warming"). The fallback shows up sooner — which makes it even more important to start fetching higher in the tree (pattern 2) rather than in siblings waiting for their turn.