Skip to content
AR logo
← Back to Blog
PerformanceNext.jsWooCommerceCore Web Vitals

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. Getting to 90+ requires a fundamentally different approach.

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.