Dashboard Skeleton Loading States
Dashboard Skeleton Loading States
All major dashboard routes in BlockManOS display animated skeleton placeholders during page navigation. This is implemented using Next.js App Router's built-in streaming SSR support via loading.tsx files and a shared skeleton component library.
How It Works
Next.js App Router treats any loading.tsx file co-located with a page.tsx as a React Suspense boundary. When a user navigates to a route, Next.js immediately renders the loading.tsx content while the page's server component fetches its data. Once the data is ready, the skeleton is swapped out for the real page — no additional code or configuration is needed in the page components themselves.
/dashboard/owners/
page.tsx ← real page (data-fetching server component)
loading.tsx ← rendered instantly as Suspense fallback
Skeleton Component Library
All skeleton layouts live in src/components/skeleton.tsx. Import them directly into any loading.tsx or custom Suspense fallback.
Skeleton
The base animated pulse block. Use it directly with a className to create any arbitrary skeleton shape.
import { Skeleton } from "@/components/skeleton";
<Skeleton className="h-8 w-64" />
Renders a rounded rectangle with a bg-muted animate-pulse style.
PageSkeleton
For generic content pages: renders a page heading area followed by a single large bordered content card.
import { PageSkeleton } from "@/components/skeleton";
export default function Loading() {
return <PageSkeleton />;
}
Used by: /dashboard/admin, /dashboard/annual-plan, /dashboard/communications, /dashboard/irish-legislation, /dashboard/owners/import, /dashboard/settings
TablePageSkeleton
For list/table pages: renders a toolbar row, a column-header row, and a configurable number of data rows.
import { TablePageSkeleton } from "@/components/skeleton";
export default function Loading() {
return <TablePageSkeleton rows={8} />;
}
| Prop | Type | Default | Description |
|---|---|---|---|
rows | number | 6 | Number of placeholder data rows to render |
Used by: /dashboard/activity (8), /dashboard/compliance (6), /dashboard/developments (8), /dashboard/gdpr (5), /dashboard/jobs (6), /dashboard/maintenance (8), /dashboard/omc (6), /dashboard/omc/cro-compliance (6), /dashboard/owners (7), /dashboard/psra (6), /dashboard/team (5)
FinancePageSkeleton
For finance and reporting pages: renders a row of 4 stat-cards followed by a main content area.
import { FinancePageSkeleton } from "@/components/skeleton";
export default function Loading() {
return <FinancePageSkeleton />;
}
Used by: /dashboard (root), /dashboard/bank-reconciliation, /dashboard/budget, /dashboard/reports, /dashboard/service-charges, /dashboard/sinking-fund
SchedulePageSkeleton
For schedule and calendar pages: renders a filter/control row followed by a card grid.
import { SchedulePageSkeleton } from "@/components/skeleton";
export default function Loading() {
return <SchedulePageSkeleton />;
}
Used by: /dashboard/agm, /dashboard/ppm
DocumentPageSkeleton
For document management pages: renders a search bar followed by a file grid.
import { DocumentPageSkeleton } from "@/components/skeleton";
export default function Loading() {
return <DocumentPageSkeleton />;
}
Used by: /dashboard/documents
Adding a Skeleton to a New Route
- Choose the skeleton component that best matches the new page's layout (or use
PageSkeletonas a general fallback). - Create a
loading.tsxfile in the same directory as the new route'spage.tsx. - Export a default
Loadingfunction that returns the chosen skeleton.
// src/app/dashboard/my-new-route/loading.tsx
import { TablePageSkeleton } from "@/components/skeleton";
export default function Loading() {
return <TablePageSkeleton rows={6} />;
}
No changes to page.tsx or any layout files are required.