Fetch in sibling components

Give each Server Component its own fetch so the fetches overlap.

From vercel-react-best-practices, rule server-parallel-fetching.

Same two fetches, two trees

Press Render page, then drag a toolbar latency.

Parent awaits firstBad
0 msserver time
  • <Page>async
    • <Sidebar>async
900 ms
Sibling componentsGood
0 msserver time
  • <Page>sync
    • <Header>async
    • <Sidebar>async
500 ms

async function Header() {
  const data = await fetchHeader()
  return <div>{data}</div>
}

async function Sidebar() {
  const items = await fetchSidebarItems()
  return <nav>{items.map(renderItem)}</nav>
}

export default function Page() {
  return (
    <div>
      <Header />
      <Sidebar />
    </div>
  )
}

Move the await into Header

Drag the highlighted line into Header, or press Enter.

page.tsxWaterfall
// new component
async function Header() {
drop the await here
return <div>{header}</div>
}
export default async function Page() {
return (
<div>
<div>{header}</div>
<Sidebar />
</div>
)
}

Press to move this line between Page and Header.

Server timelineSidebar is async
900 msserver time

The children-prop variant

Press Render page. Only the header await moves.

Layout awaits itselfBad
0 msserver time
  • <Page>sync
    • <Layout>async
      • children<Sidebar>
await fetchHeader(); return children
Renders <Header />Good
0 msserver time
  • <Page>sync
    • <Layout>sync
      • <Header>async
      • children<Sidebar>
return <><Header />{children}</>

function Layout({ children }: { children: ReactNode }) {
  return (
    <div>
      <Header />
      {children}
    </div>
  )
}

export default function Page() {
  return (
    <Layout>
      <Sidebar />
    </Layout>
  )
}

Why it matters, pros and cons

Why it matters

A parent's await blocks its children. Siblings cost the slowest fetch, not the sum.

Pros

  • Latency is max, not sum
  • Each component owns its data
  • Each sibling can stream behind Suspense

Cons

  • Real dependencies still wait; start that promise early
  • Duplicate sibling fetches need React.cache
  • One consumer of both? Promise.all is simpler