From ad32e468ac92e33fff7e9728d2670eafec164a73 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:26:16 +0800 Subject: [PATCH] perf: lazy-load heavy components, memoize SourceImage, optimize lucide-react - Add optimizePackageImports for lucide-react to next.config.ts - Wrap ClinicalDashboard with dynamic() + ssr:false in app/page.tsx - Wrap DocumentViewer with dynamic() + ssr:false in documents/[id]/page.tsx - Add route loading skeletons for root and document viewer routes - Wrap SourceImage with React.memo to prevent spurious re-renders Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- next.config.ts | 1 + src/app/documents/[id]/loading.tsx | 7 +++++++ src/app/documents/[id]/page.tsx | 7 ++++++- src/app/loading.tsx | 7 +++++++ src/app/page.tsx | 7 ++++++- src/components/ClinicalDashboard.tsx | 5 +++-- 6 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/app/documents/[id]/loading.tsx create mode 100644 src/app/loading.tsx diff --git a/next.config.ts b/next.config.ts index 1d0afeba7..517c43853 100644 --- a/next.config.ts +++ b/next.config.ts @@ -42,6 +42,7 @@ const nextConfig: NextConfig = { devIndicators: false, experimental: { cpus: 1, + optimizePackageImports: ["lucide-react"], }, poweredByHeader: false, turbopack: { diff --git a/src/app/documents/[id]/loading.tsx b/src/app/documents/[id]/loading.tsx new file mode 100644 index 000000000..81ca71167 --- /dev/null +++ b/src/app/documents/[id]/loading.tsx @@ -0,0 +1,7 @@ +export default function Loading() { + return ( +
+
+
+ ); +} diff --git a/src/app/documents/[id]/page.tsx b/src/app/documents/[id]/page.tsx index 0950bc1fb..b6de4af8b 100644 --- a/src/app/documents/[id]/page.tsx +++ b/src/app/documents/[id]/page.tsx @@ -1,4 +1,9 @@ -import { DocumentViewer } from "@/components/DocumentViewer"; +import dynamic from "next/dynamic"; + +const DocumentViewer = dynamic( + () => import("@/components/DocumentViewer").then((m) => m.DocumentViewer), + { ssr: false }, +); export default async function DocumentPage({ params, diff --git a/src/app/loading.tsx b/src/app/loading.tsx new file mode 100644 index 000000000..81ca71167 --- /dev/null +++ b/src/app/loading.tsx @@ -0,0 +1,7 @@ +export default function Loading() { + return ( +
+
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 40d4f1a75..73ba71447 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,11 @@ -import { ClinicalDashboard } from "@/components/clinical-dashboard"; +import dynamic from "next/dynamic"; import { isAppModeId, isAppModeVisible, type AppModeId } from "@/lib/app-modes"; +const ClinicalDashboard = dynamic( + () => import("@/components/clinical-dashboard").then((m) => m.ClinicalDashboard), + { ssr: false }, +); + type HomeProps = { searchParams?: Promise<{ mode?: string | string[]; diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx index eaee4b8d3..b87bee2de 100644 --- a/src/components/ClinicalDashboard.tsx +++ b/src/components/ClinicalDashboard.tsx @@ -53,6 +53,7 @@ import { import { type CSSProperties, FormEvent, + memo, useCallback, useEffect, useMemo, @@ -509,7 +510,7 @@ function subscribeAuthEmail(onStoreChange: () => void) { }; } -function SourceImage({ +const SourceImage = memo(function SourceImage({ endpoint, caption, className = "max-h-52", @@ -609,7 +610,7 @@ function SourceImage({ className={cn(className, "w-full rounded-lg object-contain")} /> ); -} +}); function ScopeAndGovernanceNotice({ scope,