Title: perf: optimise Next.js bundle and achieve green Core Web Vitals scores
Labels: frontend performance quality
Complexity: medium
Branch: perf/core-web-vitals
Depends on: FE-6, FE-7, FE-8
Problem Context
A slow frontend undermines the credibility of SoroScan as a production-grade tool. Google measures Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Interaction to Next Paint (INP) as ranking signals and user-experience indicators. Achieving green Core Web Vitals is both a UX requirement and an SEO necessity.
Scope
Included:
@next/bundle-analyzer audit and large dependency elimination
- Dynamic imports (
next/dynamic) for heavy components (charts, editors, Monaco)
- Image optimisation with
next/image across all pages
- Font optimisation with
next/font to eliminate layout shift from font loading
- Route-based code splitting verification
web-vitals library integration for reporting LCP, CLS, INP to console (dev) and analytics (prod)
- Lighthouse CI in GitHub Actions reporting scores on every PR
Not included:
- CDN / edge caching setup (infrastructure-level)
- Service worker / PWA (separate issue)
Implementation Guidelines
Files to create/update:
next.config.ts — enable bundleAnalyzer, set images.domains, configure swcMinify
src/app/layout.tsx — switch to next/font for all typefaces
src/lib/web-vitals.ts — reportWebVitals callback
src/app/page.tsx — use next/image for all hero/logo images
.github/workflows/lighthouse.yml — Lighthouse CI action
Bundle analysis workflow:
ANALYZE=true npm run build
# Opens bundle-analyzer in browser — identify chunks > 100 KB
Dynamic import pattern for heavy charts:
const EventTimelineChart = dynamic(
() => import('@/components/charts/EventTimelineChart'),
{ loading: () => <Skeleton className="h-64 w-full" />, ssr: false }
);
Lighthouse CI config:
# lighthouserc.js
module.exports = {
ci: {
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
},
},
},
};
Constraints:
- First-load JS bundle for the landing page must not exceed 200 KB (compressed)
- No layout shift from late-loading fonts or images (use
width/height on all <Image> components)
- All
dynamic() fallbacks must use shadcn <Skeleton> components to avoid CLS
Acceptance Criteria
Title:
perf: optimise Next.js bundle and achieve green Core Web Vitals scoresLabels:
frontendperformancequalityComplexity:
mediumBranch:
perf/core-web-vitalsDepends on: FE-6, FE-7, FE-8
Problem Context
A slow frontend undermines the credibility of SoroScan as a production-grade tool. Google measures Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Interaction to Next Paint (INP) as ranking signals and user-experience indicators. Achieving green Core Web Vitals is both a UX requirement and an SEO necessity.
Scope
Included:
@next/bundle-analyzeraudit and large dependency eliminationnext/dynamic) for heavy components (charts, editors, Monaco)next/imageacross all pagesnext/fontto eliminate layout shift from font loadingweb-vitalslibrary integration for reporting LCP, CLS, INP to console (dev) and analytics (prod)Not included:
Implementation Guidelines
Files to create/update:
next.config.ts— enablebundleAnalyzer, setimages.domains, configureswcMinifysrc/app/layout.tsx— switch tonext/fontfor all typefacessrc/lib/web-vitals.ts—reportWebVitalscallbacksrc/app/page.tsx— usenext/imagefor all hero/logo images.github/workflows/lighthouse.yml— Lighthouse CI actionBundle analysis workflow:
ANALYZE=true npm run build # Opens bundle-analyzer in browser — identify chunks > 100 KBDynamic import pattern for heavy charts:
Lighthouse CI config:
Constraints:
width/heighton all<Image>components)dynamic()fallbacks must use shadcn<Skeleton>components to avoid CLSAcceptance Criteria
next/imagewith explicit dimensionsnext/fontwithdisplay: swap