0PricingLogin
Next.js 15 Fullstack (App Router + Server Actions) · Lesson

Crafting Meaningful loading.tsx and Skeletons

Build route-level loading states and skeleton placeholders that match final layout to avoid shift.

Why loading.tsx Exists

In the App Router, a loading.tsx file is special: Next.js automatically wraps your route segment's page.tsx in a React <Suspense> boundary and shows loading.tsx as the fallback while the server component streams.

  • You get an instant loading state with zero manual Suspense wiring.
  • It only triggers on the server-render of that segment, not on every client interaction.
  • The fallback is shown immediately while the page's async data resolves.

This lesson is about making that fallback meaningful so the user perceives speed and the layout does not jump.

The File Convention

Drop a loading.tsx next to your page.tsx in any route segment. Its default export is rendered as the Suspense fallback for that segment and everything below it.

  • It is a normal React component — it can be a Server Component (the default) since it renders only static markup.
  • No props are passed to it; it must be self-contained.
  • Keep it lightweight so it ships and paints fast.
// app/dashboard/loading.tsx
export default function Loading() {
  return (
    <div className="dashboard-grid" aria-busy="true">
      <DashboardSkeleton />
    </div>
  );
}

All lessons in this course

  1. Suspense Boundaries and Component-Level Streaming
  2. Crafting Meaningful loading.tsx and Skeletons
  3. Partial Prerendering: Static Shell, Dynamic Holes
  4. Streaming Pitfalls: Layout Shift and Waterfalls
← Back to Next.js 15 Fullstack (App Router + Server Actions)