Streaming & Suspense

Master streaming in Next.js — how Suspense enables progressive HTML delivery, skeleton UIs, and parallel data loading for dramatically faster perceived performance.

Last reviewed: January 2025

·Next.js 13.4+Next.js 14Next.js 15
✅
Quick Understanding: Streaming sends HTML to the browser progressively — fast parts arrive immediately, slow parts stream in as they become ready. Wrap slow Server Components in <Suspense fallback={<Skeleton />}> and users see content instantly instead of waiting for all data to load.

🔍 What Is It?

Streaming in Next.js uses HTTP streaming + React Suspense to send HTML progressively:

  • →The page shell (layout, fast content) is sent immediately
  • →Slow sections (database queries, external APIs) stream in as they complete
  • →Each Suspense boundary is an independent streaming unit
  • →Users see and interact with fast content while slow content loads

Without streaming: User waits 2 seconds for ALL data before seeing ANY content.

With streaming: User sees the page shell in 100ms, fast sections in 200ms, slow sections in 2 seconds.

🎯 Why Do We Need It?

Traditional SSR blocks on the slowest data fetch. A page with 5 data fetches waits for all 5 before sending any HTML.

Streaming eliminates this:

  • →Faster Time to First Byte (TTFB) — page shell sent immediately
  • →Better perceived performance — users see content progressively
  • →Parallel data loading — independent sections load simultaneously
  • →Better UX — skeleton loaders instead of blank pages

🏗️ What Happens in Real Projects?

Streaming in a dashboard:

  • →Page shell (header, sidebar, layout) — instant, 0ms
  • →User greeting — 50ms (fast session lookup)
  • →Recent activity — 150ms (simple DB query)
  • →Revenue chart — 800ms (complex aggregation)
  • →AI insights — 2000ms (LLM API call)

With Suspense, all five load in parallel and stream in as ready. Without Suspense, the user waits 2000ms for everything.

⚙️ How It Works

Streaming rendering pipeline:

  1. Request arrives
  2. Next.js renders the page tree synchronously until it hits a Suspense boundary
  3. Sends the outer HTML (layout, fast content) to the browser immediately
  4. Browser displays the page shell + skeleton fallbacks
  5. Async Server Components inside Suspense boundaries continue rendering
  6. As each completes, Next.js streams the HTML chunk to the browser
  7. Browser replaces the skeleton with the real content (no JS re-render)

💻 Example

Streaming with Suspense Boundaries — A dashboard that streams each section independently for maximum perceived performance.

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

// Each component fetches its own data
async function RevenueChart() {
  const data = await getRevenueData(); // 800ms
  return <Chart data={data} />;
}

async function RecentOrders() {
  const orders = await getRecentOrders(); // 200ms
  return <OrdersList orders={orders} />;
}

async function UserGreeting() {
  const user = await getUser(); // 50ms
  return <h1>Welcome back, {user.name}</h1>;
}

// The page itself has NO awaits — it renders immediately
export default function DashboardPage() {
  return (
    <div className="space-y-6">
      {/* Streams in at 50ms */}
      <Suspense fallback={<GreetingSkeleton />}>
        <UserGreeting />
      </Suspense>

      <div className="grid grid-cols-2 gap-6">
        {/* Streams in at 200ms */}
        <Suspense fallback={<OrdersSkeleton />}>
          <RecentOrders />
        </Suspense>

        {/* Streams in at 800ms */}
        <Suspense fallback={<ChartSkeleton />}>
          <RevenueChart />
        </Suspense>
      </div>
    </div>
  );
}

🤔 Common Confusions

❓ What is the difference between loading.tsx and Suspense?

loading.tsx is a file convention that automatically wraps the entire page in a Suspense boundary. It shows while the whole page loads. Manual Suspense boundaries give you granular control — you can stream individual sections while the rest of the page is already visible. Use loading.tsx for simple cases, manual Suspense for fine-grained streaming.

❓ Can I use Suspense in Client Components?

Yes — Suspense works in Client Components for lazy-loaded components (React.lazy / next/dynamic). But streaming (sending HTML progressively) only works with Server Components. Client Component Suspense shows the fallback while the lazy chunk loads, not while data fetches.

❓ Does streaming affect SEO?

No — search engine crawlers wait for the full page to load before indexing. Streamed content is fully indexed. Streaming is a browser performance optimisation, not an SEO concern.

🚨 Common Production Issues

Suspense boundary too high in the tree — wrapping the entire page in one Suspense boundary gives no streaming benefit (same as no Suspense). Place boundaries around individual slow sections.

Missing Suspense around slow components — a slow Server Component without a Suspense boundary blocks the entire page. Always wrap slow data fetches in Suspense.

Skeleton shape mismatch — skeletons that look nothing like the real content cause jarring layout shifts when content loads. Match skeleton dimensions to real content dimensions.

✅ Best Practices

Place Suspense boundaries around each independently-loading section

Match skeleton shapes to real content to minimise layout shift on load

Move data fetching into the component that needs it — enables independent streaming

Use loading.tsx for simple cases, manual Suspense for fine-grained control

Measure streaming impact with Chrome DevTools waterfall view

🏛️ Architect's Note

Senior Architect's Perspective

Streaming changes the mental model from "wait for all data, then render" to "render what you have, stream the rest". This aligns with how users experience web apps — they want to see SOMETHING immediately, not a blank page for 2 seconds. Design your component tree with streaming in mind: identify slow data fetches and isolate them behind Suspense boundaries.

💬 Frequently Asked Questions

How do I stream data from a Server Action or Route Handler?

Return a ReadableStream from the Route Handler. The Vercel AI SDK uses this pattern for streaming AI responses: return new Response(stream, { headers: { "Content-Type": "text/plain; charset=utf-8", "Transfer-Encoding": "chunked" } }). On the client, use the useChat hook or manually read the stream.

Can I use useTransition with streaming?

Yes — useTransition marks a state update as non-urgent, keeping the current UI interactive while the new content streams in. Combined with Suspense, it prevents showing the fallback skeleton during navigations, keeping the old content visible until the new content is ready.

Does streaming work with static pages (SSG)?

No — static pages are pre-built HTML files. Streaming is a server-rendering feature. For static pages, the entire HTML is sent at once (it is already built). Streaming is only relevant for dynamic (SSR) routes.

🎯 Key Takeaways

1Streaming sends HTML progressively — fast parts arrive first, slow parts stream in later
2Wrap slow Server Components in <Suspense fallback={<Skeleton />}>
3Each Suspense boundary is an independent streaming unit
4loading.tsx is automatic Suspense for the whole page; manual Suspense is granular
5Move data fetching into the component that needs it to enable independent streaming
6Streaming does not affect SEO — crawlers see the full content

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Next.js references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Next.js learning path

Browse the full Next.js topic collection and learning order on SplashRide.

Next.js Interview Prep

Practice Next.js interview questions and scenario-based answers.

Frontend Engineer Career Path

A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Server Components

Master React Server Components in Next.js — the paradigm that lets components run exclusively on the server, fetch data directly, and ship zero JavaScript to the browser.

Next.js App Router Explained: Server Components, Layouts, and Routing

Learn the Next.js App Router with Server Components, nested layouts, streaming, Route Handlers, data fetching, and Pages Router migration guidance.