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
- Next.js
loading.tsxconventions - Changelog entry: v0.1.99