Rendering & DataExtra· 40 min read

Loading UI and Streaming

Show an instant skeleton while slow data loads — a loading.js file and Suspense let the page stream in piece by piece.

What you will learn

  • Add an instant loading screen with loading.js
  • Explain what streaming means in plain words
  • Stream one slow part with Suspense while the rest shows instantly

The problem: waiting on a blank screen

When a Server Component fetches data, Next.js waits for that data before it sends any HTML. If the data takes two seconds, the visitor stares at a blank screen for two seconds. That feels broken, even though it is working.

The fix is to show something immediately — a "loading…" message or a grey skeleton (a placeholder shaped like the real content) — and swap in the real content the moment it is ready.

Instant loading with a loading.js file

Next.js has a special file for this. Put a file named loading.js next to your page.js, and Next.js shows it automatically while that page is fetching. You write zero wiring code — the file name does the work.

A loading.js file shows instantly while the page beside it loads
// app/dashboard/loading.js
export default function Loading() {
  return <p>Loading your dashboard…</p>;
}

Note: Output (the moment a visitor opens /dashboard): Loading your dashboard… This appears instantly, with no blank screen. As soon as the real page.js finishes fetching its data, Next.js replaces this message with the finished page. The visitor always sees *something*.

What "streaming" actually means

Streaming means the server sends the page to the browser in pieces instead of all at once. The fast parts (your header, navigation, anything with no slow data) arrive first and show immediately. The slow parts arrive a moment later and pop into place when ready — the visitor never waits on the whole page just because one part is slow.

Think of a restaurant that brings your drink and bread right away instead of making you wait for everything until the main course is cooked. The slow dish still comes — but you are not staring at an empty table.

Streaming one slow part with Suspense

The loading.js file covers the whole page. But often only one part is slow — say a list of recent sales — while the rest of the page is instant. For that, wrap just the slow component in React Suspense and give it a fallback (what to show while it loads).

The heading shows instantly; only the slow RecentSales streams in later
// app/dashboard/page.js
import { Suspense } from 'react';

async function RecentSales() {
  const res = await fetch('https://example.com/api/sales'); // slow
  const sales = await res.json();
  return <ul>{sales.map((s) => <li key={s.id}>{s.label}</li>)}</ul>;
}

export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1>           {/* shows instantly */}
      <Suspense fallback={<p>Loading sales…</p>}>
        <RecentSales />            {/* streams in when ready */}
      </Suspense>
    </main>
  );
}

Note: Output: 1) Instantly: "Dashboard" and "Loading sales…" 2) A moment later: "Loading sales…" is replaced by the real list. The heading never waited for the sales data. Only the wrapped part showed a fallback, then streamed in when its fetch finished.

How the streaming flow works, step by step

Here is exactly what happens, in order, when a visitor opens the dashboard above:

  1. The visitor opens /dashboard. Next.js starts rendering on the server.
  2. The fast parts — the <h1>Dashboard</h1> heading — are ready instantly, so Next.js sends them to the browser right away.
  3. For the <Suspense> section, the slow RecentSales fetch is not done yet, so Next.js sends the fallback ("Loading sales…") in its place for now.
  4. The browser shows the heading and the fallback immediately — no blank screen.
  5. On the server, the RecentSales fetch finishes. Next.js streams the finished list to the browser.
  6. The browser swaps the "Loading sales…" fallback for the real list. The page is now complete, and only the truly slow part ever waited.
ToolWhat it coversUse when
loading.jsThe whole page while it loadsThe page has no fast parts to show first
<Suspense>Just the part you wrapMost of the page is fast and one piece is slow

Tip: A real skeleton (grey boxes shaped like the real cards) feels faster than the word "Loading". Build a small <SalesSkeleton /> component of grey blocks and pass it as the fallback — the layout does not jump when the real data arrives.

Watch out: Streaming only helps when the slow work is on the server (a Server Component fetch). A Client Component that fetches in useEffect runs in the browser after load, so loading.js and server Suspense do not cover it.

Q. You want the page heading to appear instantly while only a slow list streams in afterwards. What do you use?

Answer: loading.js covers the whole page. To show fast parts instantly and stream only one slow part, wrap that part in <Suspense> and give it a fallback.

✍️ Practice

  1. Add a loading.js next to a page that fetches data and watch the loading screen show instantly.
  2. Wrap one slow component in <Suspense> with a "Loading…" fallback while the rest of the page stays instant.

🏠 Homework

  1. Build a dashboard page where the heading shows instantly and a slow "recent activity" list streams in via Suspense with a grey skeleton fallback.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →