R
Rishtaara
Next.js: Zero to Full-Stack
Lesson 22 of 42Article14 min

Streaming & Loading Skeletons

Without streaming, the user waits until every slow query finishes before seeing anything. With streaming, Next.js sends a shell first, then fills in slow parts as they resolve.

Why stream?

Without streaming, the user waits until every slow query finishes before seeing anything. With streaming, Next.js sends a shell first, then fills in slow parts as they resolve.

Real-life example: Streaming is a restaurant bringing water and bread while the main course cooks — you are not staring at an empty table the whole time.

Shell first, then streamed chunks

Suspense boundaries

Wrap a slow async Server Component in <Suspense fallback={...}>. The fallback shows immediately; the child streams in when ready.

Suspense around a slow panel
import { Suspense } from "react";

async function RevenueChart() {
  const data = await fetch("https://api.example.com/revenue", {
    cache: "no-store",
  }).then((r) => r.json());
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

function ChartSkeleton() {
  return <div className="h-40 animate-pulse rounded bg-zinc-200" />;
}

export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1>
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart />
      </Suspense>
    </main>
  );
}

loading.tsx — route-level skeleton

A loading.tsx file next to page.tsx automatically wraps that page in Suspense. Instant navigation feels snappy because the skeleton shows while the page loads.

Use loading.tsx for whole-page waits. Use nested Suspense for independent panels so a slow chart does not block a fast invoice list.
app/dashboard/loading.tsx
export default function Loading() {
  return (
    <div className="space-y-4 p-6">
      <div className="h-8 w-48 animate-pulse rounded bg-zinc-200" />
      <div className="h-32 animate-pulse rounded bg-zinc-200" />
      <div className="grid gap-3 sm:grid-cols-3">
        <div className="h-24 animate-pulse rounded bg-zinc-200" />
        <div className="h-24 animate-pulse rounded bg-zinc-200" />
        <div className="h-24 animate-pulse rounded bg-zinc-200" />
      </div>
    </div>
  );
}