Skip to main content
Performance7 min read

The Next.js Performance Checklist: From 60 to 100 on Lighthouse

A practical, battle-tested checklist we use on every Next.js project - covering images, fonts, Core Web Vitals, and edge caching strategies.

Halsoft Team

Engineering

Performance is SEO. Google's Core Web Vitals are ranking factors, and user experience directly impacts conversion rates. Here's the checklist we run on every Next.js project before launch - the same one we used to build the site you're reading right now.

Images: The Biggest Quick Win

Images are the #1 cause of poor LCP (Largest Contentful Paint) scores. Our rules:

  • Always use <Image> from next/image - it handles lazy loading, responsive sizing, and format conversion (WebP/AVIF) automatically
  • Set explicit width and height to prevent layout shift (CLS)
  • Use priority on above-the-fold images (hero, logo)
  • Use sizes attribute to help the browser pick the right resolution
  • For decorative backgrounds, prefer CSS gradients or SVGs over raster images

Fonts: Swap, Don't Block

Font loading is the second most common performance killer. With Next.js, use next/font - it automatically hosts fonts locally (no Google Fonts roundtrip) and sets font-display: swap.

Limit yourself to 2 font families max. Each additional font family adds ~100-200KB and a render-blocking request. We typically use one sans-serif for body and one for headings.

JavaScript: Ship Less

  • Use Server Components by default - only add "use client" when you need interactivity
  • Lazy-load heavy components with dynamic() and loading fallbacks
  • Defer analytics and third-party scripts with strategy="afterInteractive" or "lazyOnload"
  • Audit your bundle with @next/bundle-analyzer - you'll be surprised what's hiding in there

Caching: Edge Everything

Next.js App Router makes caching powerful but complex. Our approach:

  • Static pages (marketing, blog) - build-time rendered, cached at CDN edge
  • Dynamic pages (dashboards) - stream with loading.tsx skeletons
  • API routes - set appropriate Cache-Control headers
  • Images and fonts - immutable cache with long max-age

Core Web Vitals Targets

For every project, we aim for:

  • LCP < 2.5s (ideally < 1.5s)
  • FID/INP < 100ms
  • CLS < 0.1

Measure with real-user monitoring (Google Search Console, web-vitals library), not just Lighthouse. Lab scores and field scores often differ significantly.

The Meta-Principle

Performance isn't a feature you add at the end - it's a constraint you design around from the start. Every architectural decision, every dependency, every image should pass the question: "Is this worth the bytes?"

Need Help With Your Project?

We build the kind of software we write about. Let's talk about yours.