All Docs
FeaturesCalmony Sanctions MonitorUpdated March 12, 2026

Fixing Blank Screens: Route-Level Loading Boundaries in the Dashboard

Fixing Blank Screens: Route-Level Loading Boundaries in the Dashboard

Version: 0.1.99 · Category: Developer Experience / UX

The problem

If you navigated between sections of the dashboard — say, from People to Matches — you may have noticed the layout chrome (sidebar, header) appeared immediately while the main content area stayed blank for a brief moment. This happened because no loading.tsx files existed anywhere in the route tree.

Next.js relies on these files to provide a structured loading fallback during server-side navigation. Without them, the framework has no skeleton or spinner to show while a server component fetches its data — the layout simply renders with an empty content slot.

This was tracked internally as control ERR-03 and affected the following routes:

  • /dashboard
  • /dashboard/people
  • /dashboard/matches
  • /dashboard/billing

The fix

We added loading.tsx files to the dashboard root and each of the key sub-routes listed above. Each file exports a lightweight skeleton UI (or a centred spinner) that Next.js renders automatically while the page's data is in flight.

src/app/dashboard/
├── loading.tsx          ← new
├── people/
│   └── loading.tsx      ← new
├── matches/
│   └── loading.tsx      ← new
└── billing/
    └── loading.tsx      ← new

Because these files integrate with React Suspense, Next.js can also begin streaming server component responses — sending the layout shell to the browser immediately and flushing content as it resolves, rather than waiting for all data before sending anything.

What you'll notice

  • No more blank content area. Navigating between dashboard sections now shows a skeleton placeholder instantly.
  • Faster perceived load times. Suspense streaming means the browser starts rendering earlier, even on slower connections or when upstream data sources are slow.
  • Consistent experience. All primary dashboard routes now behave the same way during navigation.

Implementation notes for contributors

If you add a new route segment under src/app/dashboard/, consider adding a loading.tsx alongside it. A minimal implementation looks like this:

// src/app/dashboard/my-new-route/loading.tsx
export default function Loading() {
  return (
    <div className="flex h-full items-center justify-center">
      <span className="animate-spin h-6 w-6 rounded-full border-2 border-muted border-t-foreground" />
    </div>
  );
}

For data-heavy pages, a skeleton that mirrors the shape of the real content is preferred over a generic spinner — it reduces layout shift and gives users a clearer sense of what is loading.

Related