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,