The 12-Point Next.js Production Checklist
Everything I run through before shipping a Next.js app to production — from image budgets to caching headers to Lighthouse regressions.
Shipping is a feature
You can build the prettiest Next.js app in the world, but if it takes 4s to paint on a mid-range Android, nobody sees it. Here's the exact list I run through before every launch.
1. Set an image budget
No image above 200KB, no page above 1MB of total imagery. Use next/image everywhere and pre-generate AVIF/WebP.
2. Kill the "largest text" LCP trap
Preload your hero font, use font-display: swap, and — controversially — avoid custom fonts on the fold if they add 200ms.
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], display: 'swap' })
3. Use route segment config
export const dynamic = 'force-static'
export const revalidate = 3600
Sprinkle these on pages that don't need SSR. Free CDN.
4. Ship an error boundary
A plain error.js in the app router with a "Try again" button covers 95% of runtime failures.
5. Loading skeletons, not spinners
loading.js → skeleton → real content. Users don't tolerate spinners past 400ms.
6. Cache your third-party fetches
Wrap slow upstream APIs with 10-minute in-memory caches at minimum.
7. Compress everything
Enable brotli at the edge. Vercel does this for you — self-hosting? Set it in your reverse proxy.
8. Set up sane robots + sitemap
export default function sitemap() {
return [{ url: 'https://you.dev', lastModified: new Date() }]
}
9. Monitor Real User Metrics
Vercel Speed Insights or a self-hosted alternative. Synthetic Lighthouse lies about your actual users' devices.
10. Ship an offline-first 500 page
A static app/not-found.js and a friendly error.js — both should look like the rest of your site.
11. Environment guards
Every process.env.WHATEVER should be read once at module load and validated with Zod. Fail fast, not at runtime.
12. Lighthouse gate in CI
If your PR drops the score below 95, block the merge. Yes, really.
Have a great launch 🚀