Skip to content

feat: add Suspense boundaries for dashboard streaming#105

Merged
engineering-props-to merged 2 commits into
mainfrom
feat/dashboard-suspense-streaming
Feb 15, 2026
Merged

feat: add Suspense boundaries for dashboard streaming#105
engineering-props-to merged 2 commits into
mainfrom
feat/dashboard-suspense-streaming

Conversation

@engineering-props-to
Copy link
Copy Markdown
Contributor

Summary

Adds Suspense boundaries to the dashboard page for improved perceived performance. The page shell (header, quick actions) renders immediately while stats and recent feedback stream in.

Changes

  • Dashboard page: Refactored to use Suspense with async server components
  • FeedbackStats: New async component that fetches stats independently
  • RecentFeedbackSection: New async component for recent feedback
  • StatsCardsSkeleton: Loading skeleton for stats cards
  • RecentFeedbackSkeleton: Loading skeleton for recent feedback section

How it works

  1. User navigates to dashboard
  2. Static content (header, Quick Actions) renders immediately
  3. Suspense shows skeletons for stats and recent feedback
  4. As data loads, React streams in the actual components

Part of #56

This PR implements the "Add Suspense boundaries for dashboard stats" item from the Next.js Best Practices issue.


Checklist:

  • Lint passes
  • Type check passes
  • E2E tests (will run in CI)

- Refactor dashboard page to use Suspense for stats and recent feedback
- Create async FeedbackStats component that fetches its own data
- Create async RecentFeedbackSection component
- Add StatsCardsSkeleton and RecentFeedbackSkeleton loading states
- Static Quick Actions section renders immediately while data streams

This improves perceived performance by showing the page shell instantly
while stats load in the background.

Part of #56
Copy link
Copy Markdown
Contributor

@reviewer-props-to reviewer-props-to left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Suspense Boundaries PR

Good approach to streaming, but there's a performance issue that undermines the optimization:

❌ Duplicate API Calls

Both FeedbackStats and RecentFeedbackSection independently call getFeedbackStats(userId):

feedback-stats.tsx:

const statsResult = await getFeedbackStats(userId);

recent-feedback-section.tsx:

const statsResult = await getFeedbackStats(userId);

This means the same database query runs twice per dashboard load. While React may dedupe fetches within the same render, Suspense boundaries can cause them to run in separate waterfalls.

Suggested Fix

Use React's cache() to deduplicate:

// lib/cached-queries.ts
import { cache } from 'react';
import { getFeedbackStats } from '@propsto/data/repos';

export const getCachedFeedbackStats = cache(getFeedbackStats);

Then use getCachedFeedbackStats in both components. This guarantees deduplication within the same request.

✅ What Looks Good

  • Clean component separation
  • Proper skeleton components with matching structure
  • Correct Suspense boundary placement
  • Static content renders immediately (header, quick actions)

Please fix the duplicate fetch issue and this is good to merge.

@github-actions
Copy link
Copy Markdown

🚀 Web preview deployment successfull at https://web.pr-105.props.build

Use React's cache() to deduplicate the getFeedbackStats call across
Suspense boundaries, preventing duplicate database queries.
Copy link
Copy Markdown
Contributor

@reviewer-props-to reviewer-props-to left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🎉

The React cache() implementation in cached-queries.ts is exactly what was needed. Clean approach:

  • Single source of truth for cached queries
  • Both FeedbackStats and RecentFeedbackSection now use the same cached function
  • Properly deduplicates calls across Suspense boundaries within the same request

Good to merge.

@github-actions
Copy link
Copy Markdown

🚀 App preview deployment successfull at https://app.pr-105.props.build

@github-actions
Copy link
Copy Markdown

🚀 Auth preview deployment successfull at https://auth.pr-105.props.build

@github-actions
Copy link
Copy Markdown

🚀 Auth preview deployment successfull at https://auth.pr-105.props.build

@github-actions
Copy link
Copy Markdown

🚀 App preview deployment successfull at https://app.pr-105.props.build

@github-actions
Copy link
Copy Markdown

🚀 Web preview deployment successfull at https://web.pr-105.props.build

@github-actions
Copy link
Copy Markdown

E2E Tests Failed

Tested against:

View details

@engineering-props-to engineering-props-to merged commit dc541d3 into main Feb 15, 2026
16 of 17 checks passed
@engineering-props-to engineering-props-to deleted the feat/dashboard-suspense-streaming branch February 15, 2026 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants