Streaming with Suspense
Suspense boundaries mark the slow parts
<Suspense>, and give it a fallback. Next.js sends the surrounding page immediately, with the fallback in place of the wrapped component, then streams in the real content once the component's data is ready — swapping it into place in the already-loaded page.app/dashboard/page.tsx
import { Suspense } from 'react'
import Header from './header'
import RevenueChart from './revenue-chart'
export default function DashboardPage() {
return (
<div>
<Header />
<Suspense fallback={<p>Loading revenue…</p>}>
<RevenueChart />
</Suspense>
</div>
)
}app/dashboard/revenue-chart.tsx — the slow piece
import { getRevenueData } from '@/libs/analytics'
export default async function RevenueChart() {
// Imagine this query takes 2-3 seconds against a data warehouse.
const data = await getRevenueData()
return <Chart data={data} />
}<Header /> renders instantly since it has no data dependency, and the browser shows "Loading revenue…" in place of the chart the moment the page arrives — rather than a fully blank tab for two or three seconds. When getRevenueData() resolves, Next.js streams the real chart markup down the same connection and React swaps it in, no client-side refetch required.Multiple independent boundaries
You can — and usually should — use several Suspense boundaries on one page, so a slow section doesn't hold up a different slow section. Each boundary streams in independently as soon as its own data is ready.
app/dashboard/page.tsx — independent boundaries
<div>
<Header />
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders />
</Suspense>
</div>RecentOrders query would delay RevenueChart too, even if the chart's own data was ready first — a single shared boundary streams in only once every component inside it is ready.Why this improves perceived performance
Streaming lets the server send parts of a page as soon as they're ready, instead of waiting for every data dependency to resolve first.
Wrap a slow component in
<Suspense fallback={...}>; Next.js sends the fallback immediately and streams in the real content once it resolves.Use multiple independent Suspense boundaries so one slow section doesn't block another from streaming in on its own.
The total load time may be similar, but perceived performance improves because something useful renders immediately.
Suspense boundaries wrap the smallest component that actually needs the slow data, not the whole page.