React Streaming

Progressive loading with Suspense boundaries that streams content as it becomes available. Perfect for Dashboards, Real-time applications, and Progressive apps where users benefit from seeing content incrementally.

Server

🌐
Request
Streaming
πŸ–₯️
Server receives request
Server starts rendering
Server renders fast content
Server streams HTML chunk 1
<Suspense>
Server renders slow content (Suspense)
Server streams HTML chunk 2

Client

Client receives and displays chunk 1
Client receives and displays chunk 2
React.hydrateRoot()
✨Page interactive
Benefits x Trade-offs

Benefits

  • β€’Progressive Loading: Fast content appears immediately
  • β€’Better UX: Users see content as it becomes available
  • β€’Parallel Loading: Multiple components load independently
  • β€’Error Boundaries: Isolated error handling per component

Trade-offs

  • β€’Incremental UI: Layout may shift as content loads
  • β€’Fallback UX needed: Must design loading states
  • β€’Complexity: Managing multiple Suspense boundaries
  • β€’SEO considerations: Some content loads after initial render
How to implement?

Streaming uses Suspense boundaries with async Server Components. Cannot use hooks in Suspense boundaries.

import { Suspense } from "react";

export default async function StreamingPage() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <AsyncComponent />
      </Suspense>
    </div>
  );
}

async function AsyncComponent() {
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();
  return <div>{data.message}</div>;
}
  • β€’Components are async functions (no 'use client' directive)
  • β€’Cannot use React hooks (including use hook) in Server Components
  • β€’Wrap async components with Suspense and provide fallback
  • β€’Use loading.js for route-level loading states
  • β€’Content streams to the client as it becomes available