Streaming & Suspense

Send HTML Pieces As They Become Ready

Streaming & Suspense

Wrap slow parts of the page in `<Suspense>` so the rest renders immediately and the slow bits stream in as their data resolves.

4 min read Level 3/5 #nextjs#streaming#suspense
What you'll learn
  • Wrap a slow component in `<Suspense fallback>`
  • Show fast parts instantly while async work resolves
  • Improve TTFB and LCP measurably

If one part of a page takes 2 seconds to fetch, you do not have to make the entire page wait. React’s <Suspense> plus streaming SSR lets Next.js send the rest of the HTML immediately, and pipe in the slow part when it is ready.

Wrap the Slow Part

import { Suspense } from 'react'
import { SlowList } from './SlowList'
import { Skeleton } from './Skeleton'

export default function Page() {
  return (
    <main>
      <h1>Dashboard</h1>
      <p>Welcome back.</p>
      <Suspense fallback={<Skeleton />}>
        <SlowList />
      </Suspense>
    </main>
  )
}

The header and welcome text appear instantly. The skeleton renders in SlowList’s place until its data arrives, then the real content swaps in — all in the same HTTP response.

The Slow Component Just Awaits

export async function SlowList() {
  const items = await fetch('/api/slow', { cache: 'no-store' }).then(r => r.json())
  return <ul>{items.map((i: { id: string; name: string }) => <li key={i.id}>{i.name}</li>)}</ul>
}

No special API. The combination of async server component plus a <Suspense> boundary above it is what enables streaming.

Why It Matters

Time-to-first-byte improves because Next can start sending HTML before any data is loaded. Largest contentful paint often improves because the header and hero render first.

For whole-page boundaries on navigation, there is an even simpler primitive: loading.tsx.

loading.tsx — Instant Skeleton →