Skip to content
Alan Regaya
← Back to Blog

Core Web Vitals at Scale: Lessons from 30+ Production Sites

·8 min read

The Real Problem with WooCommerce Performance

After optimizing 30+ production WooCommerce sites, I've learned that the standard advice — enable caching, compress images, use a CDN — gets you to a 70 Lighthouse score. Pushing past 90 means rethinking the render path itself.

Here's what actually works at scale.

Critical CSS Inlining

The single biggest LCP win on static Next.js sites is eliminating render-blocking CSS. With the critters library in the postbuild step, above-the-fold styles are inlined into the HTML and the full stylesheet is loaded asynchronously.

// next.config.ts
experimental: {
  optimizeCss: true, // uses critters internally
}

For custom implementations, a postbuild script using Critters directly gives more control over which routes get inlined and what threshold to use for "above the fold".

Image Pipeline: WebP at Build Time

Rather than relying on Next.js Image's on-demand optimization (which adds latency on first load), I pre-convert all screenshots and hero images to WebP at build time using Sharp:

// scripts/convert-screenshots.mjs
await sharp(src)
  .resize(960, undefined, { fit: 'inside', withoutEnlargement: true })
  .webp({ quality: 80 })
  .toFile(dest);

This runs as a prebuild script. WebP files are gitignored (generated artifacts), PNGs stay in source control.

Font Loading: The Swap vs Block Tradeoff

Using next/font with display: 'swap' eliminates FOIT but can cause CLS if the fallback font has different metrics. The fix: use adjustFontFallback: false and manually set size-adjust in the font-face fallback.

For monospace fonts used in code blocks, preload: false avoids an unnecessary preload hint for a font that's only visible after scroll.

Eliminating Third-Party JS

Every third-party script — analytics, chat widgets, cookie banners — adds to INP. On the portfolio itself, there is zero third-party JavaScript. Analytics can wait; the user experience cannot.

For client sites where analytics is non-negotiable, next/script with strategy="afterInteractive" defers execution until after the page is interactive.

Measuring What Matters

Lighthouse scores are a proxy. The real metric is field data from CrUX. A page can score 95 in Lighthouse and still have poor field LCP if the server is slow for real users.

The workflow: optimize locally with Lighthouse, verify with PageSpeed Insights (which uses real CrUX data), then monitor with Core Web Vitals in Search Console.

Get in Touch

Let's work together

Open to full-stack roles. I'm particularly interested in headless e-commerce, performance-critical Next.js apps, and projects that push modern web capabilities.

Philippines · Remote