Streaming in Next.js means the server sends a page's HTML in chunks as each part becomes ready, instead of waiting for the whole page to render. The browser paints the first chunk immediately, then fills in the rest as it arrives.
This matters when one part of a page is slow. Without streaming, a slow database query delays the entire page. With streaming, the fast parts reach the browser first while the slow part shows a fallback until its data is ready.
How Next.js streams
Next.js streams through React Suspense. When a component suspends, React renders its fallback and sends it in the initial chunk, then keeps rendering the rest.
When the slow work finishes, its finished HTML arrives in a later chunk and swaps into place. The fallback itself is real markup in the very first response, not something a client script adds afterwards.
There are two ways to create these boundaries: a loading file at the route level, and a Suspense boundary around a specific component. Both rely on the same React behavior.
// app/dashboard/page.tsx
import { Suspense } from "react";
import { PostFeed, Weather } from "./components";
export default function Dashboard() {
return (
<section>
<Suspense fallback={<p>Loading feed</p>}><PostFeed /></Suspense>
<Suspense fallback={<p>Loading weather</p>}><Weather /></Suspense>
</section>
);
}PostFeed and Weather are async components that each fetch their own data. Each Suspense boundary streams independently: the dashboard shell renders at once, then the feed and weather fill in as each finishes. If weather is slow, the feed still appears first.
Blocking rendering vs streaming
In blocking rendering, the server waits for every fetch before sending a single byte, and the browser shows nothing until then. Streaming replaces that one long wait with many smaller waits, one per boundary. The total work is the same, but the time before the first paint is not.
| Blocking render | Streaming | |
|---|---|---|
| First byte | After the slowest fetch | After the shell renders |
| What arrives first | Nothing | Shell and fallbacks |
| One slow query | Delays the whole page | Delays only its boundary |
What the browser sees
On a slow connection, the user sees the shell and fallbacks first, then the resolved sections replace them. The order in which sections appear depends on how fast each one resolves, not on their order in the source.
Each boundary is also a hydration unit. React hydrates the streamed sections independently and prioritizes whatever the user interacts with, instead of hydrating the whole page in one blocking pass.
Crawlers are handled differently. Next.js detects HTML-limited bots by user agent and waits for the full render, sending one finished document rather than a shell with fallbacks, so those bots never index a skeleton.
Boundaries add a small amount of coordination overhead, so wrap only the genuinely slow pieces. Splitting every component into its own boundary makes the page flicker without a real speed win.
For the route-level fallback, see loading.js and instant loading states, and for controlling each boundary see Suspense boundaries.
Streaming requires a server runtime that supports it, so it is not available with a static export. It also depends on what sits in front of that server: a reverse proxy, CDN, or compression layer that buffers the response can collect every chunk before forwarding it, which hides the benefit even though the server streamed correctly.
Rune AI
Key Insights
- Streaming sends HTML in chunks instead of one complete response.
- The browser paints the first chunk, then fills in the rest.
- Suspense boundaries and loading files create the chunk boundaries.
- Slow work shows a fallback while the rest of the page renders.
- Streaming needs a server runtime and does not work with static export.
Frequently Asked Questions
Does streaming require a special server?
What is the difference between streaming and server-side rendering?
Do I need to enable streaming?
Conclusion
Streaming sends HTML in chunks as each part of a page becomes ready. Suspense boundaries and loading files are what split the page into those chunks, so slow work no longer blocks the whole route.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.