Skip to content

Performance Optimisation & Core Web Vitals #841

Description

@DokaIzk

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.tsreportWebVitals 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

  • Lighthouse Performance score ≥ 90 on the landing page
  • LCP ≤ 2.5 s, CLS ≤ 0.1, INP ≤ 200 ms on desktop
  • First-load JS ≤ 200 KB (gzip) on the landing page
  • All images use next/image with explicit dimensions
  • All fonts loaded via next/font with display: swap
  • Lighthouse CI action blocks PRs that regress Performance below 90



Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions