A storefront we rebuilt in 2025 went from 4.2 seconds to 190 milliseconds time-to-interactive. No magic — just a checklist we now run on every engagement. Here is that checklist, in the order it pays off.
The 190ms recipe
The single biggest lever is moving static work off the origin. Every page that can be prerendered should be, and every dynamic page should stream. That combination alone accounts for roughly half the gains on any legacy Next.js app.
// next.config.ts — the baseline we ship
export default {
images: {
formats: ["image/avif", "image/webp"],
minimumCacheTTL: 31536000,
},
experimental: {
optimizePackageImports: ["lucide-react"],
},
}Then, in priority order:
- Preload fonts with font-display: swap and inline the critical CSS.
- Serve AVIF/WebP from the CDN edge — never the origin server.
- Cache API responses at the edge with stale-while-revalidate windows.
- Keep the first bundle under 100KB gzipped; code-split everything else.
Measure before and after with the same Lab + Field mix, or you'll be optimizing for a dashboard instead of for people.
The last 100 milliseconds cost more than the first 3 seconds, but they're also what separates a fast site from a suspiciously fast site. Users can feel the difference.