Skip to the solution
MediumFrontend9 min read

What is loading.js in Next.js and how does Suspense streaming work?

237 views

The problem

Question presented to candidate: "Our product page takes about a second and a half to render because of a slow API. Users stare at a blank screen. What does adding loading.tsx change, and how does streaming actually work under the hood?"

What a strong answer should cover:

  • loading.tsx becomes a React <Suspense> boundary around the segment page (inside the layout), with the loading component as the fallback.
  • With it, the server sends the layout and the fallback in the first chunk and streams the page HTML into the same response when it is ready; without it, nothing is sent until the page finishes.
  • On client navigation the fallback is prefetched, so the URL changes and the skeleton shows immediately, and the shared layout stays interactive.
  • Manual <Suspense> boundaries give finer control: independent parts stream in any order as each one resolves.
  • Limits: loading.tsx does not cover the layout of the same segment, so awaiting request data at the top of that layout still blocks; the status code is fixed (200) once streaming starts.
  • Streaming is server-rendered HTML, so it does not hurt SEO; static export does not support it.

Clarifying questions expected:

  • "Is the slow part in the page or in the layout?" β€” loading.tsx only helps with the page and below.
  • "Do all parts of the page depend on the slow call?" β€” if not, smaller Suspense boundaries show more content sooner.

Code / implementation expected: Yes β€” a loading.tsx, a page with two Suspense boundaries, and a layout that keeps request data inside its own boundary.

  • nextjs
  • loading
  • suspense
  • streaming

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 8 sections

Read the code

loading.tsx, granular Suspense, and a non-blocking layout
// app/dashboard/loading.tsx
// Becomes <Suspense fallback={<Loading />}> around page.tsx. Streamed in the first chunk.
export default function Loading() {
  return <p aria-busy="true">Loading dashboard...</p>;
}

// app/dashboard/page.tsx
import { Suspense } from "react";
import { connection } from "next/server";
import { getActivity, getStats } from "../lib/data";

async function Stats() {
  const s = await getStats();
  return <p>{s.users} users</p>;
}
async function Activity() {
  const items = await getActivity();
  return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>;
}

// The page itself renders at once; each slow part streams in when ready, in any order.
export default async function Dashboard() {
  await connection(); // request-time data
  return (
    <>
      <h1>Dashboard</h1>
      <Suspense fallback={<p>Loading stats...</p>}><Stats /></Suspense>
      <Suspense fallback={<p>Loading activity...</p>}><Activity /></Suspense>
    </>
  );
}

// app/dashboard/layout.tsx
import { Suspense } from "react";
import { cookies } from "next/headers";

// Do not await request data at the top of a layout: loading.tsx sits INSIDE the layout,
// so it could not show until the layout finished. Push the read into its own boundary.
async function UserBadge() {
  const name = (await cookies()).get("name")?.value ?? "guest";
  return <span>Hi {name}</span>;
}

export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <section>
      <header><Suspense fallback={<span>...</span>}><UserBadge /></Suspense></header>
      {children}
    </section>
  );
}

// app/lib/data.ts
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
export async function getStats() { await wait(300); return { users: 1200 }; }
export async function getActivity() { await wait(1200); return ["signup", "upgrade"]; }

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 85 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

How do you fetch data in Next.js Server Components?

Continue