Skip to main content
theordinarycompany
Technology
TechnologyUpdated: 9 min read

Next.js Performance Checklist: 20 Optimizations for 2026

A comprehensive Next.js performance checklist covering Core Web Vitals, server components, caching, and more. Get 90+ Lighthouse scores.

Core Web Vitals Basics

Google cares about three metrics. You should too.

LCP (Largest Contentful Paint): How fast does the main content appear? Target: under 2.5 seconds.

What helpsWhat hurts
Server renderingLarge JavaScript bundles
Image optimizationUnoptimized images
Fast hostingSlow API calls blocking render

INP (Interaction to Next Paint): How responsive is the page to clicks? Target: under 200ms.

What helpsWhat hurts
Minimal JavaScriptHeavy client-side processing
useTransition for slow updatesBlocking the main thread
DebouncingSynchronous work on every keystroke

CLS (Cumulative Layout Shift): Does stuff jump around while loading? Target: under 0.1.

What helpsWhat hurts
Explicit image dimensionsImages without width/height
Placeholder space for dynamic contentInserting content above existing content
Font-display: optionalMassive font-display: swap changes

Server Components Done Right

Next.js 13+ made server components the default. Use them correctly.

Default to server components. They render on the server, send only HTML, and don't add to your JavaScript bundle.

Only use 'use client' when necessary. Anything interactive needs to be a client component: forms, buttons with handlers, components using hooks.

Push client boundaries down. Don't make a whole page a client component because one button needs onClick. Make just the button a client component.

PatternGood or bad
Page as server component, interactive bits as clientGood
Entire page as client componentUsually bad
Server component fetching data, passing to client componentGood
Client component fetching dataOften unnecessary

Images and Fonts

Images are usually the biggest performance problem.

Use next/image. Always. It handles lazy loading, responsive sizes, and modern formats automatically.

The key props:

PropWhen to use
priorityAbove-the-fold images, especially LCP
sizesWhen image isn't full-width
placeholder="blur"When you have a blur placeholder

Use next/font. It handles font loading optimization automatically, including preventing CLS.

Import fonts once in your layout, use throughout the app. Don't load the same font multiple times.


Caching Strategies

Next.js caching is powerful but confusing. Here's the simple version:

Static pages (default): Built at deploy time, served from CDN. Maximum performance.

Revalidating pages: Static but refresh periodically. Use when content changes occasionally.

Dynamic pages: Rendered on every request. Use when content is personalized or real-time.

ScenarioStrategy
Blog postStatic
Product page with inventoryRevalidate every 5 minutes
User dashboardDynamic
HomepageRevalidate hourly

For data fetching, Next.js caches fetch() calls by default. Override when needed:

NeedSolution
Cache foreverfetch(url) (default)
Cache for 1 hourfetch(url, { next: { revalidate: 3600 }})
Never cachefetch(url, { cache: 'no-store' })

Bundle Size

Smaller bundles = faster loads.

Check your bundle. Install @next/bundle-analyzer and run a build with ANALYZE=true. You'll see exactly what's making your bundle big.

Common culprits:

  • Moment.js (use date-fns or dayjs instead)
  • Lodash (import only what you need)
  • Large UI libraries (do you need all of it?)
  • Unused dependencies

Dynamic imports for heavy components. If you have a chart library, video player, or editor that's only used on some pages, dynamic import it.

Tree shaking depends on import style:

Import styleTree shaking
import { format } from 'date-fns'Yes
import * as dateFns from 'date-fns'No
import dateFns from 'date-fns'No

Quick checklist

Run through this before shipping:

  • Images use next/image with explicit dimensions
  • LCP image has priority prop
  • Fonts use next/font
  • Pages that can be static are static
  • Bundle analyzer shows no obvious waste
  • Third-party scripts are lazy loaded
  • Core Web Vitals pass in Lighthouse
  • No layout shift visible during load

If your Lighthouse score is under 90, something on this list is probably the cause.

📬 Get Engineering Insights

Practical articles on MVP development, legacy modernization, and building products that scale. Delivered to your inbox.

No spam. Unsubscribe anytime. We respect your privacy.

TOC

The Ordinary Company

Product-minded engineers helping startups build and scale. 50+ projects delivered.

🚀

Ready to Build Your Project?

Let's discuss how we can help bring your ideas to life. Free consultation, no strings attached.