#nextjs #streaming #suspense #ui

Day 12 — Streaming & Suspense: Instant UI

🧭 Day 12 — Streaming & Suspense: Instant UI

Zero to Hero — Hands-on Next.js Tutorial

Problem: With traditional SSR, the user sees a blank white screen until everything is ready (DB calls, API fetches, etc.). If one slow query takes 3 seconds, the user waits 3 seconds for the entire page.

Solution: Streaming. Send the parts that are ready (Header, Sidebar) immediately. Send the slow parts (Charts, Feed) later, when they finish.


🟦 1. loading.tsx (Page Level)

The easiest way to stream is to add a loading.tsx file in your route folder. Next.js will automatically wrap your page.tsx in a React Suspense boundary.

// src/app/dashboard/loading.tsx
export default function Loading() {
  return <div className="text-xl font-bold">Loading Dashboard... ⏳</div>;
}

Now, when you visit /dashboard, you see “Loading Dashboard…” instantly. When the async page.tsx finishes fetching, the loading UI is swapped with the real content.


🟩 2. Suspension (Component Level)

What if you have a fast part (User Profile) and a slow part (Sales Chart) on the same page? Don’t block the Profile waiting for the Chart!

Wrap the slow component in <Suspense>.

import { Suspense } from 'react';
import SalesChart from './SalesChart'; // This component fetches its own data

export default function Dashboard() {
  return (
    <main>
      <h1>My Dashboard</h1>
      
      {/* This loads instantly */}
      <UserProfile /> 

      {/* This shows "Loading Chart" until SalesChart is ready */}
      <Suspense fallback={<p>Loading Chart...</p>}>
        <SalesChart />
      </Suspense>
    </main>
  );
}

🟧 3. Benefits of Streaming

  1. TTFB (Time To First Byte): Decreases massively. The server responds immediately.
  2. FCP (First Contentful Paint): The user sees the layout (Sidebar/Nav) instantly.
  3. TTI (Time To Interactive): React hydrates the visible parts while the hidden parts are still loading.

🧪 Challenge: Day 12

  1. Create a component SlowComponent that awaits a new Promise(resolve => setTimeout(resolve, 3000)).
  2. Put it in your page. Notice the whole page hangs for 3s.
  3. Create a loading.tsx next to the page.
  4. Notice how you see the loading state instantly now?
  5. Remove loading.tsx and try wrapping just the component in <Suspense fallback={...}>.

See you tomorrow for Error Handling! 🚨