feat: add Suspense boundaries for dashboard streaming#105
Conversation
- 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
reviewer-props-to
left a comment
There was a problem hiding this comment.
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.
|
🚀 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.
reviewer-props-to
left a comment
There was a problem hiding this comment.
LGTM! 🎉
The React cache() implementation in cached-queries.ts is exactly what was needed. Clean approach:
- Single source of truth for cached queries
- Both
FeedbackStatsandRecentFeedbackSectionnow use the same cached function - Properly deduplicates calls across Suspense boundaries within the same request
Good to merge.
|
🚀 App preview deployment successfull at https://app.pr-105.props.build |
|
🚀 Auth preview deployment successfull at https://auth.pr-105.props.build |
|
🚀 Auth preview deployment successfull at https://auth.pr-105.props.build |
|
🚀 App preview deployment successfull at https://app.pr-105.props.build |
|
🚀 Web preview deployment successfull at https://web.pr-105.props.build |
|
❌ E2E Tests Failed Tested against: |
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
How it works
Part of #56
This PR implements the "Add Suspense boundaries for dashboard stats" item from the Next.js Best Practices issue.
Checklist: