From ae0f6b8ffab3d2f87305cdc9228bd84597b2c1bc Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Tue, 10 Mar 2026 00:39:35 +0100 Subject: [PATCH] Fix dashboard and map tab lag with memoization and code splitting Memoize createMap() in DottedMap (was regenerating 8000 SVG dots every render) and convert 11 heavy dashboard components to next/dynamic imports so the page shell renders instantly instead of blocking on one massive render pass. Co-Authored-By: Claude Opus 4.6 --- app/sites/[id]/page.tsx | 24 +++++++++++++----------- components/dashboard/DottedMap.tsx | 7 +++++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/sites/[id]/page.tsx b/app/sites/[id]/page.tsx index e6bed47..a35afa2 100644 --- a/app/sites/[id]/page.tsx +++ b/app/sites/[id]/page.tsx @@ -22,20 +22,22 @@ import { getDateRange, formatDate } from '@ciphera-net/ui' import { toast } from '@ciphera-net/ui' import { Button } from '@ciphera-net/ui' import { Select, DatePicker, DownloadIcon } from '@ciphera-net/ui' +import dynamic from 'next/dynamic' import { DashboardSkeleton, useMinimumLoading } from '@/components/skeletons' -import ExportModal from '@/components/dashboard/ExportModal' -import ContentStats from '@/components/dashboard/ContentStats' -import TopReferrers from '@/components/dashboard/TopReferrers' -import Locations from '@/components/dashboard/Locations' -import TechSpecs from '@/components/dashboard/TechSpecs' -import Chart from '@/components/dashboard/Chart' -import PerformanceStats from '@/components/dashboard/PerformanceStats' -import GoalStats from '@/components/dashboard/GoalStats' -import ScrollDepth from '@/components/dashboard/ScrollDepth' -import Campaigns from '@/components/dashboard/Campaigns' import FilterBar from '@/components/dashboard/FilterBar' import AddFilterDropdown, { type FilterSuggestion, type FilterSuggestions } from '@/components/dashboard/AddFilterDropdown' -import EventProperties from '@/components/dashboard/EventProperties' + +const Chart = dynamic(() => import('@/components/dashboard/Chart')) +const ContentStats = dynamic(() => import('@/components/dashboard/ContentStats')) +const TopReferrers = dynamic(() => import('@/components/dashboard/TopReferrers')) +const Locations = dynamic(() => import('@/components/dashboard/Locations')) +const TechSpecs = dynamic(() => import('@/components/dashboard/TechSpecs')) +const PerformanceStats = dynamic(() => import('@/components/dashboard/PerformanceStats')) +const GoalStats = dynamic(() => import('@/components/dashboard/GoalStats')) +const ScrollDepth = dynamic(() => import('@/components/dashboard/ScrollDepth')) +const Campaigns = dynamic(() => import('@/components/dashboard/Campaigns')) +const EventProperties = dynamic(() => import('@/components/dashboard/EventProperties')) +const ExportModal = dynamic(() => import('@/components/dashboard/ExportModal')) import { type DimensionFilter, serializeFilters, parseFiltersFromURL } from '@/lib/filters' import { useDashboardOverview, diff --git a/components/dashboard/DottedMap.tsx b/components/dashboard/DottedMap.tsx index f5cca77..94197b3 100644 --- a/components/dashboard/DottedMap.tsx +++ b/components/dashboard/DottedMap.tsx @@ -25,7 +25,10 @@ export default function DottedMap({ data, className }: DottedMapProps) { const dotRadius = 0.25 const [tooltip, setTooltip] = useState<{ x: number; y: number; country: string; pageviews: number } | null>(null) - const { points, addMarkers } = createMap({ width, height, mapSamples: 8000 }) + const { points, addMarkers } = useMemo( + () => createMap({ width, height, mapSamples: 8000 }), + [width, height], + ) const markerData = useMemo(() => { if (!data.length) return [] @@ -48,7 +51,7 @@ export default function DottedMap({ data, className }: DottedMapProps) { () => markerData.map((d) => ({ lat: d.lat, lng: d.lng, size: d.size })), [markerData], ) - const processedMarkers = addMarkers(markerInputs) + const processedMarkers = useMemo(() => addMarkers(markerInputs), [addMarkers, markerInputs]) // Compute stagger helpers const { xStep, yToRowIndex } = useMemo(() => {