All Docs
FeaturesDepositClearUpdated March 11, 2026

Faster, Smoother Pages: Suspense Boundaries & Loading Skeletons

Faster, Smoother Pages: Suspense Boundaries & Loading Skeletons

Release: v0.1.151 · Track: Performance (PERF-07)


The Problem

When navigating around the dashboard — whether to Tenancies, Properties, Deductions, Deposit Release, or Compliance — the app previously showed a blank screen or an unstyled flash while waiting for data to load. This happened because:

  1. No loading.tsx files existed in any of the 30+ route segments in the Next.js App Router.
  2. The only <Suspense> boundary (on the Analytics page) had no fallback prop, so React rendered nothing while the component suspended.
  3. Streaming SSR was not being used, meaning the browser had to wait for the full server response before rendering anything visible.

The result: layout shifts, blank screens, and an experience that felt slower than it actually was.


The Fix

1. loading.tsx Files Across All Major Routes

Next.js App Router automatically renders a loading.tsx file as an instant fallback while a route segment's data is being fetched. We've added these to every major section of the dashboard:

src/app/dashboard/loading.tsx
src/app/dashboard/tenancies/loading.tsx
src/app/dashboard/properties/loading.tsx
src/app/dashboard/deductions/loading.tsx
src/app/dashboard/deposit-release/loading.tsx
src/app/dashboard/compliance/loading.tsx

Each file exports a skeleton component that mirrors the layout of the real page — same card shapes, same column structure, same spacing — so the transition from loading to loaded feels seamless rather than jarring.

2. Skeleton Components

Every skeleton is purpose-built to match its page. For example, the Properties skeleton renders placeholder cards in the same grid as real property listings; the Tenancies skeleton mirrors the table rows users expect to see. This eliminates layout shifts (CLS) as data arrives.

3. Suspense Fallback on Analytics

The Analytics page already used <Suspense>, but without a fallback prop — meaning React silently rendered nothing during the suspend. This is now fixed:

// Before
<Suspense>
  <AnalyticsComponent />
</Suspense>

// After
<Suspense fallback={<AnalyticsSkeleton />}>
  <AnalyticsComponent />
</Suspense>

The AnalyticsSkeleton renders immediately and is replaced once the analytics data has resolved.

4. Streaming SSR Enabled

With loading.tsx in place, Next.js App Router can now stream the page shell to the browser right away, then stream in the data-dependent content as it becomes ready on the server. Users see meaningful content faster — without waiting for every data fetch to complete before anything renders.


What You'll Notice

  • No more blank screens when navigating between dashboard sections.
  • Instant visual feedback — a skeleton matching the page layout appears within milliseconds of navigation.
  • Smoother transitions — content appears in-place as data arrives, with no layout jumps.
  • Faster perceived load times across all major routes.

Technical Notes for Developers

  • loading.tsx files are a Next.js App Router convention. They wrap the route segment in an implicit <Suspense> boundary automatically — no manual <Suspense> wrapper is needed around page.tsx.
  • Skeleton components should be kept lightweight (no data fetching, no heavy imports) to ensure they render as fast as possible.
  • When adding new route segments in future, always add a corresponding loading.tsx to maintain this pattern.
  • Streaming SSR works best when data fetches are initiated at the page.tsx level using async components or use() hooks, allowing React to suspend at the right boundary.

Released in v0.1.151. Part of the ongoing PERF frontend track.