Streaming allows you to send HTML to the client progressively, showing content as it becomes ready rather than waiting for everything.
How Streaming Works
- Server starts rendering - Begins processing the React tree
- Shell is ready - Static parts (layout, navigation) are sent immediately
- Content streams in - Dynamic parts are sent as they complete
- Client hydrates - JavaScript makes the page interactive
The Shell
The "shell" is everything outside Suspense boundaries:
tsxasync function Page() { return ( <html> <body> {/* Shell - sent immediately */} <Header /> <nav>Navigation</nav> {/* Streamed content */} <Suspense fallback={<MainSkeleton />}> <MainContent /> </Suspense> {/* Shell - sent immediately */} <Footer /> </body> </html> ); }
Streaming Timeline
Time 0ms: Shell sent (Header, Nav, Skeletons, Footer)
Time 100ms: <UserProfile /> streams in, replaces skeleton
Time 300ms: <RecentPosts /> streams in, replaces skeleton
Time 500ms: <Recommendations /> streams in, replaces skeleton
Nested Suspense for Granular Loading
tsxasync function Dashboard() { return ( <div className="dashboard"> {/* Level 1: Main layout */} <Suspense fallback={<DashboardSkeleton />}> <DashboardLayout> {/* Level 2: Independent sections */} <Suspense fallback={<StatsSkeleton />}> <Stats /> </Suspense> <Suspense fallback={<ChartSkeleton />}> <AnalyticsChart /> </Suspense> <Suspense fallback={<TableSkeleton />}> <RecentActivity /> </Suspense> </DashboardLayout> </Suspense> </div> ); }
Loading UI Patterns
Skeleton Screens
tsxfunction PostSkeleton() { return ( <div className="post-skeleton"> <div className="skeleton-title" /> <div className="skeleton-line" /> <div className="skeleton-line" /> <div className="skeleton-line short" /> </div> ); } <Suspense fallback={<PostSkeleton />}> <Post id={postId} /> </Suspense>
Instant Loading States (Next.js)
tsx// app/dashboard/loading.tsx export default function Loading() { return <DashboardSkeleton />; } // Automatically wraps page.tsx in Suspense
Benefits of Streaming
- Faster Time to First Byte (TTFB) - Don't wait for slow data
- Better perceived performance - Users see content sooner
- Improved Core Web Vitals - Better LCP scores
- Graceful degradation - Slow components don't block fast ones
Streaming vs Traditional SSR
| Aspect | Traditional SSR | Streaming SSR |
|---|---|---|
| First content | After all data loads | Immediately |
| Slow queries | Block everything | Only block their section |
| User experience | All or nothing | Progressive |
| TTFB | Slow | Fast |