Learn / Frameworks / Next.js / Streaming and Loading UI

Next.js · Lesson 7 of 15

Streaming and Loading UI

Show shells instantly and stream the rest with Suspense.

  • Intermediate
  • 15 min read
  • 3 objectives

Before this lessonLesson 6: Data Fetching and Caching

What you will learn

  • Add loading.tsx
  • Split slow parts into Suspense
  • Avoid blocking the whole page

Your Progress

0 of 15 lessons 0%

  • Lessons0 / 15
  • Completed0
  • Est. time left~ 4 hours

Create a free account to keep your progress on every device.

A slow database call should not blank the whole page. Stream a shell immediately, then fill in the slow parts as they finish.

loading.tsx is a boundary

A loading.tsx next to a page.tsx wraps that page in Suspense. Navigation shows the fallback instantly while the page awaits data.

// app/dashboard/loading.tsx
export default function Loading() {
  return <p>Loading dashboard…</p>;
}

Split the slow bits

If only one widget is slow, do not put the await in the page. Extract it and wrap that component in Suspense so the rest of the page paints first.

// app/dashboard/page.tsx
import { Suspense } from "react";
import { Revenue } from "./revenue";
import { RecentOrders } from "./orders";

export default function Dashboard() {
  return (
    <>
      <h1>Dashboard</h1>
      <Suspense fallback={<p>Loading revenue…</p>}>
        <Revenue />
      </Suspense>
      <Suspense fallback={<p>Loading orders…</p>}>
        <RecentOrders />
      </Suspense>
    </>
  );
}

// revenue.tsx — a server component
export async function Revenue() {
  const data = await getRevenue();   // slow
  return <p>This month: {data.total}</p>;
}

Skeletons beat spinners

Match the fallback layout to the final UI (grey boxes of the same size). The page feels faster because nothing jumps.

Up next · Lesson 8Middleware, Cookies and SessionsRun code at the edge for auth redirects and read cookies on the server.