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>fromnext/image- it handles lazy loading, responsive sizing, and format conversion (WebP/AVIF) automatically - Set explicit
widthandheightto prevent layout shift (CLS) - Use
priorityon above-the-fold images (hero, logo) - Use
sizesattribute 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()andloadingfallbacks - 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.tsxskeletons - API routes - set appropriate
Cache-Controlheaders - 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?"