From 493b644735e0c9135562a267c39d37d36dad3892 Mon Sep 17 00:00:00 2001 From: Akash Jadhav <142928804+Akash1510@users.noreply.github.com> Date: Tue, 26 May 2026 17:09:56 +0530 Subject: [PATCH 1/2] perf: lazy load below-the-fold dashboard widgets --- src/app/dashboard/page.tsx | 308 ++++++++++++++++++++-------------- src/components/LazyWidget.tsx | 65 +++++++ 2 files changed, 244 insertions(+), 129 deletions(-) create mode 100644 src/components/LazyWidget.tsx diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 89ba88f5..a3e2e7e5 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -1,3 +1,4 @@ +import LazyWidget from "@/components/LazyWidget"; import DiscussionsWidget from "@/components/DiscussionsWidget"; import CommunityMetrics from "@/components/CommunityMetrics"; import GoalTracker from "@/components/GoalTracker"; @@ -26,62 +27,84 @@ const SkeletonCard = () => ( const ContributionGraphSkeleton = () => (
-

Your Commits

+

+ Your Commits +

); const PRMetricsSkeleton = () => (
-

PR Analytics

+

+ PR Analytics +

); const CodingActivityInsightsCard = dynamic( () => import("@/components/CodingActivityInsightsCard"), - { ssr: false, loading: () => } + { ssr: false, loading: () => }, ); -const FriendComparison = dynamic(() => import("@/components/FriendComparison"), { - ssr: false, - loading: () => , -}); +const FriendComparison = dynamic( + () => import("@/components/FriendComparison"), + { + ssr: false, + loading: () => , + }, +); -const ActivityRingChart = dynamic(() => import("@/components/ActivityRingChart"), { - ssr: false, - loading: () => , -}); +const ActivityRingChart = dynamic( + () => import("@/components/ActivityRingChart"), + { + ssr: false, + loading: () => , + }, +); -const ContributionGraph = dynamic(() => import("@/components/ContributionGraph"), { - ssr: false, - loading: () => , -}); +const ContributionGraph = dynamic( + () => import("@/components/ContributionGraph"), + { + ssr: false, + loading: () => , + }, +); -const ContributionHeatmap = dynamic(() => import("@/components/ContributionHeatmap"), { - ssr: false, - loading: () => , -}); +const ContributionHeatmap = dynamic( + () => import("@/components/ContributionHeatmap"), + { + ssr: false, + loading: () => , + }, +); const PRMetrics = dynamic(() => import("@/components/PRMetrics"), { ssr: false, loading: () => , }); -const PRBreakdownChart = dynamic(() => import("@/components/PRBreakdownChart"), { - ssr: false, - loading: () => , -}); +const PRBreakdownChart = dynamic( + () => import("@/components/PRBreakdownChart"), + { + ssr: false, + loading: () => , + }, +); const CommitTimeChart = dynamic(() => import("@/components/CommitTimeChart"), { ssr: false, loading: () => , }); -const PRReviewTrendChart = dynamic(() => import("@/components/PRReviewTrendChart"), { - ssr: false, - loading: () => , -}); +const PRReviewTrendChart = dynamic( + () => import("@/components/PRReviewTrendChart"), + { + ssr: false, + loading: () => , + }, +); import WeeklySummaryCard from "@/components/WeeklySummaryCard"; import { AIMentorWidget } from "@/components/AIMentorWidget"; import ExportButton from "@/components/ExportButton"; @@ -106,128 +129,155 @@ export default async function DashboardPage() {
-
- - Year in Code - - - Settings - - -
- - -
- -
-
-
-

Your Year in Code is here! ✨

-

Discover your top languages, longest streaks, and coding habits of the year.

-
-
- View Wrapped +
+ + Year in Code + + + Settings + + +
+ + +
+ +
+
+
+

+ Your Year in Code is here! ✨ +

+

+ Discover your top languages, longest streaks, and coding + habits of the year. +

+
+
+ View Wrapped +
+
+
-
-
-
- -
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
- {/* Row 1: Contribution graph + Streak + Local Coding Time */} -
-
- -
- -
-
- + {/* Row 1: Contribution graph + Streak + Local Coding Time */} +
+
+ +
+ }> + + +
+
+ }> + + +
-
-
- - -
- +
+ + +
+ +
-
- {/* Row 2: PR metrics, community metrics, PR breakdown & Time Chart */} -
- - - - -
- {/* Row 2b: Activity Ring Chart */} -
- -
+ {/* Row 2: PR metrics, community metrics, PR breakdown & Time Chart */} +
+ + + + +
+ {/* Row 2b: Activity Ring Chart */} +
+ +
-
- -
+
+ +
-
- -
+
+ }> + + +
- {/* Row 3: Issue metrics + CI analytics */} -
-
- + {/* Row 3: Issue metrics + CI analytics */} +
+
+ }> + + +
+ }> + + +
+ {/* Row 3b: Discussion activity */} +
+ }> + +
- -
- {/* Row 3b: Discussion activity */} -
- -
- {/* Row 4: Pinned repositories */} -
- -
+ {/* Row 4: Pinned repositories */} +
+ }> + + +
- {/* Row 5: Inactive repository reminder */} -
- -
+ {/* Row 5: Inactive repository reminder */} +
+ }> + + +
- {/* Row 6: Top repos + Language breakdown + Goal tracker */} -
- - - -
+ {/* Row 6: Top repos + Language breakdown + Goal tracker */} +
+ }> + + + }> + + + +
- {/* Row 7: Recent GitHub activity */} -
- + {/* Row 7: Recent GitHub activity */} +
+ }> + + +
-
); } diff --git a/src/components/LazyWidget.tsx b/src/components/LazyWidget.tsx new file mode 100644 index 00000000..21a8ec18 --- /dev/null +++ b/src/components/LazyWidget.tsx @@ -0,0 +1,65 @@ +"use client"; + +import {ReactNode,useEffect,useRef,useState,memo} from "react"; + +interface LazyWidgetProps { + children: ReactNode; + fallback: ReactNode; + rootMargin?: string; + threshold?: number; + className?: string; +} + +function LazyWidget({ + children, + fallback, + rootMargin = "300px", + threshold = 0.1, + className = "", +}: LazyWidgetProps) { + + const containerRef = useRef(null); + const observerRef = useRef(null); + const [isVisible , setIsVisible] = useState(false); + + useEffect(()=>{ + const node = containerRef.current; + if(!node || isVisible) return; + + observerRef.current = new IntersectionObserver(([entry])=>{ + if(entry.isIntersecting){ + setIsVisible(true); + + if(observerRef.current){ + observerRef.current.disconnect(); + observerRef.current = null; + } + } + },{ + root : null, + rootMargin, + threshold + }); + + observerRef.current.observe(node); + + return ()=>{ + if(observerRef.current){ + observerRef.current.disconnect(); + observerRef.current = null; + } + }; + },[isVisible,rootMargin,threshold]); + + return ( +
+ + {isVisible ? children : fallback} +
+ ); +} + +export default memo(LazyWidget); \ No newline at end of file From 594de9782f42acc8d2a32b3ef684f0b3c5bbe151 Mon Sep 17 00:00:00 2001 From: Akash Jadhav <142928804+Akash1510@users.noreply.github.com> Date: Wed, 27 May 2026 13:58:03 +0530 Subject: [PATCH 2/2] perf: lazy load below-the-fold dashboard widgets --- src/app/dashboard/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index a5c0da7d..42ac73b9 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -112,7 +112,7 @@ import ExportButton from "@/components/ExportButton"; import Link from "next/link"; import PersonalRecords from "@/components/PersonalRecords"; import LocalCodingTime from "@/components/LocalCodingTime"; -import CodingTimeCard from "@/components/CodingTimeCard"; +import CodingTimeWidget from "@/components/CodingTimeWidget"; import RecentActivity from "@/components/RecentActivity"; import { authOptions } from "@/lib/auth"; import { getServerSession } from "next-auth"; @@ -202,7 +202,7 @@ export default async function DashboardPage() {
- +