Paweł Sobolewski
Paweł Sobolewski
Senior Software Engineer

Next.js Streaming Explained: Faster Rendering vs CSR & SSR

Oct 01, 20257 min read

Modern web apps need to load fast - users won’t wait for entire pages to render before engaging with content. Traditional methods like client-side rendering often introduce delays, especially when data fetching depends on large JavaScript bundles. Next.js streaming, powered by React Server Components and Suspense, solves this by progressively sending page content to the browser as it becomes ready. The result: faster time-to-first-content and a smoother user experience.

Why Use Streaming in Next.js?

Streaming isn't just another performance optimization - it solves specific problems in modern frontend development. Users today demand immediate feedback, but traditional rendering approaches often create delays that make interfaces feel unresponsive.

By allowing the server to send parts of a page to the browser as they become ready, streaming eliminates waiting for complete page rendering. Users can view and interact with meaningful content almost immediately. This approach not only enhances perceived performance but also eliminates the frustration of watching loading spinners or staring at empty placeholders.

Imagine this scenario: You have a large web page where most content is either static or quickly retrieved. However, one component depends on a slow external API. Without streaming, the entire page would be delayed while waiting for that single request. With streaming, the majority of the page appears immediately, with only the slower component arriving later. For users, this creates a fast, responsive experience, even when certain elements take longer to load.

Next.js Streaming vs Client-Side Rendering (CSR)

At first glance, streaming and client-side rendering seem to share a common goal: delivering content to users quickly without waiting for the entire page to load. However, they solve this problem in fundamentally different ways.

Client-Side Rendering (CSR)

With CSR, the server sends only a minimal HTML shell and a JavaScript bundle. Since client components cannot be asynchronous, they can't effectively fetch data during rendering. Instead, developers must rely on useEffect for any asynchronous operations.

This approach has three major drawbacks:

  1. Larger bundles – more logic and components must be sent to the browser.
  2. Delayed data fetching – network requests can only start after the JavaScript has been downloaded, parsed, executed, and the component reaches the useEffect stage. This significantly delays data retrieval compared to server-side methods.
  3. Tricky security - because data fetching happens on the client, sensitive information like API keys may need to be exposed or handled carefully.

Streaming

Streaming takes the opposite approach. With rendering and data fetching happening on the server, the application can start retrieving data immediately. As soon as any part of the page is ready, it's sent to the browser and displayed - no need to wait for the entire UI. This approach reduces time to first meaningful paint and eliminates the "waterfall" effect common in CSR.

How Next.js Streaming Works with React Server Components

In Next.js, streaming leverages React Server Components and Suspense to progressively send HTML to the browser instead of waiting for the entire page to render.

Here's the process:

  1. Initial render: The server immediately sends HTML for ready-to-display elements (layout, header, placeholders, etc.).
  2. Suspense boundaries: Components requiring slower data (API calls, database queries) are wrapped in special <Suspense /> component, showing fallbacks like spinners or skeletons rather than blocking the entire page.
  3. Progressive hydration: Once data resolves, the server streams the completed HTML for that component, seamlessly replacing the fallback in the browser.
  4. JS hydration (in parallel): Simultaneously, client-side JavaScript gradually hydrates the page to make it fully interactive.

Using the React use Hook for Streaming in Next.js

Next.js also enables streaming through the use hook, which lets React components directly unwrap promises inside Client Components.

The common implementation pattern works like this:

  • A Server Component initiates the async operation and passes the promise as a prop.
  • A Client Component utilizes the use hook to consume the data, suspending itself until the data is ready.

Why this matters:

  • With traditional streaming, the streamed component must be a Server Component that fetches data. Client Components, by default, cannot be asynchronous, which prevents them from directly participating in streaming.
  • The use hook solves this limitation: it enables Client Components to consume promises and suspend. This means you can stream data and immediately add interactivity (such as event handlers and UI state) without needing to split logic between server and client.
  • This gives developers greater flexibility: data loading still begins early on the server, while the final rendering and interactive elements can reside in Client Components.

Does Next.js Streaming Require JavaScript?

It's important to understand that streaming doesn't eliminate the need for JavaScript in the browser. While the server handles data fetching and initial HTML rendering, the browser still needs JavaScript to replace temporary placeholders - like spinners or skeletons - with the actual content as it arrives.

Without JavaScript, users would only see the static placeholders, preventing the progressive content delivery from functioning, even though the server has already generated this content.

In essence, streaming speeds up content delivery, but JavaScript remains essential for swapping temporary elements with the final rendered components.

Next.js Streaming Example with Code (SSR vs CSR vs Streaming)

Repository link: https://github.com/u11d-com/blog_nextjs-streaming

To demonstrate streaming benefits in a real-world context, I've built a very simple Next.js demo application that fetches and displays a list of funny jokes. This project compares three rendering strategies side by side: traditional Server-Side Rendering (SSR), Client-Side Rendering (CSR), and the newer React Server Components with streaming (with server and client components approaches).

The application features separate pages for each rendering approach:

  • SSR Page: Renders everything on the server and delivers complete HTML in a single response. Users see content only after the server processes the entire page.
  • CSR Page: Delivers a minimal HTML shell while JavaScript handles all data fetching on the client side. Users see loading states until data fetching and rendering complete.
  • Streaming Page: Leverages React Server Components to progressively send UI parts to the browser as they become ready. This allows users to interact with visible content while slower components continue loading.
  • Streaming Page with use Hook: Uses the React use hook to stream data directly into Client Components. This enables not only progressive rendering but also immediate interactivity, since Client Components can both consume async data and attach event handlers without waiting for a full page load.

The application includes a built-in analytics system that tracks rendering times for each page section, helping visualize the performance differences between SSR, CSR, and streaming approaches.

EventSSR (ms)CSR (ms)STREAMING (ms)STREAMING WITH USE (ms)
Fetching jokes started328143
Joke list rendered2451254522552418
Page rendered2722281272285

You can fetch and run the application Yourself, but let’s talk about results and what they mean.

Next.js Performance Benchmark: SSR vs CSR vs Streaming

Our benchmark clearly shows how each rendering strategy differs in data fetching timing and content delivery.

SSR (Server-Side Rendering)

  • Fetching jokes started: ~3 ms (almost immediately)
  • Joke list rendered: ~2451 ms
  • Page rendered: ~2722 ms

Analysis: While data fetching begins instantly, users see nothing until all data is processed. This creates a slower first paint, despite the server's early work.

CSR (Client-Side Rendering)

  • Fetching jokes started: ~281 ms
  • Page rendered: ~281 ms (initial skeleton/spinner)
  • Joke list rendered: ~2545 ms

Analysis: With CSR, data fetching waits for JavaScript to load (~281 ms). Users see a placeholder quickly but wait over 2.5 seconds for actual content.

Important: Larger, more complex pages mean bigger JS bundles that take longer to execute. This delays data fetching even further, increasing time to meaningful content and hurting user experience.

Streaming

  • Fetching jokes started: ~4 ms
  • Page rendered: ~272 ms
  • Joke list rendered: ~2255 ms

Analysis: Streaming offers the best experience: data fetching starts immediately, initial content appears in ~272 ms, and slower components load progressively.

Key advantage over CSR: Streaming doesn't depend on JavaScript bundle size to begin fetching data. This allows even complex pages to start loading meaningful content almost instantly, dramatically improving perceived performance.

Conclusion: Why Streaming is Best for Next.js Performance

Comparing SSR, CSR, and Streaming in Next.js reveals distinct trade-offs for each approach:

  • SSR begins data fetching immediately on the server, but delivers a slower time-to-first-content for users.
  • CSR postpones data fetching until after JavaScript bundle execution, with larger pages further extending this delay - resulting in the longest wait for meaningful content.
  • Streaming offers the best of both worlds: nearly instant data fetching, quick initial content rendering, and progressive component delivery. Because it doesn't depend on bundle size to start loading data, streaming ensures fast perceived performance even with complex pages.

Bottom line: For modern Next.js applications where user experience and speed are priorities, streaming provides the most responsive and scalable solution.

FAQ Section

Frequently Asked Questions (FAQ) about Next.js Streaming

Q: What is streaming in Next.js?

A: Streaming is a rendering technique where the server sends parts of a page to the browser as soon as they’re ready. Instead of waiting for the entire page to render, users see meaningful content almost immediately, improving perceived performance.

Q: How is streaming different from client-side rendering (CSR)?

A: CSR waits until the JavaScript bundle loads before fetching data, delaying meaningful content. Streaming starts data fetching on the server instantly and progressively streams components, making the page feel faster and more responsive.

Q: Does streaming replace SSR (Server-Side Rendering)?

A: No. Streaming builds on SSR by adding progressive delivery. SSR sends a complete page after rendering, while streaming sends it in chunks, letting users see content earlier.

Q: Do I still need JavaScript with streaming?

A: Yes. While the server handles data fetching and initial HTML, JavaScript is required to replace placeholders with the actual streamed content and hydrate the UI for interactivity.

Q: What is the use hook in streaming?

A: The use hook allows React Client Components to directly consume promises. This means you can stream data into interactive Client Components without splitting logic between server and client.

Q: When should I use streaming in my Next.js app?

A: Use streaming when your page contains a mix of fast-loading and slow-loading content. It ensures users can interact with available content immediately while slower components continue to load in the background.

RELATED POSTS
Michał Miler
Michał Miler
Senior Software Engineer

Medusa Checkout Flow: Step-by-Step Guide to Building a Complete E-Commerce Checkout

Sep 01, 20257 min read
Article image
Michał Miler
Michał Miler
Senior Software Engineer

How to Implement Secure Authentication in NestJS with Stytch

Aug 27, 20258 min read
Article image
Kacper Drzewicz
Kacper Drzewicz
Senior Software Engineer

Headless CMS in E-Commerce: How to Integrate Medusa for Scalable, Content-Rich Online Stores

Aug 13, 20254 min read
Article image