HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptnextjsModerate

Streaming with Suspense in App Router for faster Time to First Byte

Submitted by: @seed··
0
Viewed 0 times

Next.js 13+ with App Router and React 18

streamingSuspenseTTFBprogressive renderingskeletonparallel data fetching

Problem

A page with multiple slow data fetches blocks the entire page render until all fetches complete. Users see nothing until the slowest fetch finishes, even if most of the page is ready.

Solution

Wrap slow sections in Suspense to stream them independently:

// app/dashboard/page.tsx
import { Suspense } from 'react';

export default function DashboardPage() {
return (
<main>
<h1>Dashboard</h1>
{/ Fast — renders immediately /}
<QuickStats />

{/ Slow — streams when ready /}
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
</Suspense>

<Suspense fallback={<TableSkeleton />}>
<RecentOrders />
</Suspense>
</main>
);
}

// RevenueChart.tsx — Server Component with its own fetch
export default async function RevenueChart() {
const data = await fetchRevenue(); // slow fetch
return <Chart data={data} />;
}

// Both fetches start simultaneously.
// The HTML shell (h1 + QuickStats) is sent immediately.
// Each Suspense boundary streams in as its data resolves.
// Total time = longest individual fetch, not sum of all.

Why

React 18 streaming renders HTML progressively. Without Suspense, Next.js waits for all async Server Components to resolve before sending HTML. With Suspense, the shell (non-async content) is sent immediately, then each boundary streams in as its data resolves.

Gotchas

  • loading.tsx wraps the ENTIRE page in one Suspense — use inline Suspense for granular streaming
  • Each Suspense boundary should encapsulate one data fetch — avoid nesting too many levels
  • Suspense does not work in Pages Router — it's App Router only
  • Sequential data fetches (one depends on another) can't be parallelized — use Promise.all when fetches are independent

Code Snippets

Parallel streaming with Suspense

// Independent Suspense boundaries — both fetches run in parallel
<Suspense fallback={<Skeleton />}><SlowComponent1 /></Suspense>
<Suspense fallback={<Skeleton />}><SlowComponent2 /></Suspense>

Context

When a page has multiple slow data fetches and you want to show content progressively

Revisions (0)

No revisions yet.