Engineering

Building fast Next.js sites without losing the plot

How we approach performance budgets, image strategy, and edge rendering to ship marketing sites that load in under a second — even on shaky mobile networks.

Code on a dark editor screen
ByMidlajJuly 15, 20266 min read

Start with a budget, not a framework

Before writing a single line of code, we set a performance budget: a target Largest Contentful Paint under 1.2 seconds on a mid-range Android over a throttled 4G connection. Every decision after that — fonts, images, third-party scripts — gets measured against the budget, not against what feels reasonable in isolation.

Frameworks are tools, not answers. Next.js gives you the primitives (streaming, partial prerendering, the Image component) but it will happily let you ship a 3MB bundle if you don't watch it.

The image strategy that actually moves the needle

Nine times out of ten, images are the largest thing on the page. We convert everything to AVIF with a WebP fallback, generate three or four sizes, and let the browser pick. The Next.js Image component handles the plumbing, but only if you give it accurate width and height — otherwise you'll pay for layout shift.

For hero images that need to appear immediately, we mark them priority and inline a low-quality placeholder. For everything below the fold, native lazy loading is enough.

Third-party scripts are the silent killer

Analytics, chat widgets, tag managers — each one looks harmless in isolation. Together they turn a fast site into a slow one. We load them with the afterInteractive strategy, defer anything non-critical, and periodically audit what's actually earning its place on the page.

Measure in production, not in Lighthouse

Lighthouse scores are a starting point. Real users have older phones, worse networks, and browser extensions that Lighthouse doesn't simulate. We wire up real-user monitoring on day one so we can see what actual visitors experience, and we treat regressions like bugs.

All posts