DEV Community

Magevanta
Magevanta

Posted on • Originally published at magevanta.com

Magento 2 Core Web Vitals: How to Score 90+ on PageSpeed

Core Web Vitals are a Google ranking signal and a direct measure of your store's user experience. Most Magento stores score 30–50 on mobile PageSpeed. Here's how to get to 90+.

Understanding what actually matters

Google measures three Core Web Vitals:

Metric What it measures Good threshold
LCP (Largest Contentful Paint) How fast the main content loads < 2.5s
INP (Interaction to Next Paint) How responsive the page is to clicks < 200ms
CLS (Cumulative Layout Shift) How stable the layout is < 0.1

For most Magento stores, LCP is the biggest problem — slow server response time and large unoptimized hero images.

Fix 1: Time to First Byte (TTFB)

LCP starts with TTFB. If your server takes 800ms to respond, you've burned most of your LCP budget before the browser even starts rendering.

Target TTFB: < 200ms

The main levers:

  • Full page cache (Redis-backed FPC)
  • Server location (use a CDN edge node close to users)
  • Reduced PHP execution time (module optimization, opcode cache)
# Check your current TTFB
curl -w "@curl-format.txt" -o /dev/null -s "https://your-store.com/"
# curl-format.txt: time_starttransfer: %{time_starttransfer}\n
Enter fullscreen mode Exit fullscreen mode

Fix 2: LCP image optimization

The LCP element on most product and category pages is the hero image or first product image. Common mistakes:

No preload: The browser discovers the LCP image late because it's in CSS or lazy-loaded.

<!-- Add this in your <head> for the LCP image -->
<link rel="preload" as="image" href="/media/catalog/product/hero.webp" fetchpriority="high" />
Enter fullscreen mode Exit fullscreen mode

Wrong format: JPEG/PNG instead of WebP or AVIF.

<!-- Use <picture> for modern format support -->
<picture>
  <source srcset="hero.avif" type="image/avif" />
  <source srcset="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="Product" width="800" height="600" loading="eager" />
</picture>
Enter fullscreen mode Exit fullscreen mode

Missing dimensions: Images without width and height attributes cause layout shifts (CLS) as they load.

Too large: A 3000px wide image served to a 400px mobile screen wastes bandwidth. Use srcset:

<img
  src="product-400.webp"
  srcset="product-400.webp 400w, product-800.webp 800w, product-1200.webp 1200w"
  sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
  width="800" height="600"
  alt="Product name"
/>
Enter fullscreen mode Exit fullscreen mode

Fix 3: Eliminate render-blocking resources

Every <script> and <link rel="stylesheet"> in <head> blocks rendering. Magento loads a lot of them by default.

Scripts: Use defer or async for non-critical JS.

<!-- Bad -->
<script src="bundle.js"></script>

<!-- Good -->
<script src="bundle.js" defer></script>
Enter fullscreen mode Exit fullscreen mode

CSS: Inline critical CSS (above-the-fold styles) and load the rest asynchronously:

<style>/* critical CSS inlined here */</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
Enter fullscreen mode Exit fullscreen mode

In Magento, you can configure this in requirejs-config.js and theme layout XML.

Fix 4: JavaScript bundle size

Magento ships with RequireJS and a large JS bundle. On a cold page load, the browser must download, parse, and execute all of it before the page is interactive.

Quick wins:

  • Disable unused Magento JS modules in requirejs-config.js
  • Enable JS bundling and minification: bin/magento setup:static-content:deploy -f
  • Use Magento's built-in JS bundler or a custom Webpack/Vite build
# Enable JS/CSS merging and minification
bin/magento config:set dev/js/enable_js_bundling 1
bin/magento config:set dev/js/minify_files 1
bin/magento config:set dev/css/minify_files 1
Enter fullscreen mode Exit fullscreen mode

Target: < 200KB of JavaScript on initial load (before user interaction).

Fix 5: CLS — prevent layout shifts

CLS > 0.1 is a ranking penalty. Common causes in Magento:

Images without dimensions: Always add width and height to <img> tags. This lets the browser reserve space before the image loads.

Web fonts causing FOUT: Use font-display: swap and preload your fonts:

<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin />
Enter fullscreen mode Exit fullscreen mode

Late-loading banners/widgets: If a header banner loads 500ms after the page, everything below it shifts down. Reserve space with min-height on the container.

Cookie consent banners: These are a major CLS source. Pre-allocate space or animate them in from outside the viewport.

Fix 6: Reduce third-party script impact

Analytics, chat widgets, review platforms, and ad tags are often responsible for 30–50% of a Magento store's JavaScript load.

Audit your third-party scripts:

  1. Open DevTools → Network → JS, filter by third-party domains
  2. Measure the size and load time of each
  3. Defer anything that doesn't need to run on initial load

For chat widgets and social proof tools: load them on first user interaction (scroll or mousemove event) instead of on page load.

Measuring and tracking progress

Use these tools:

  • Google PageSpeed Insights — real-world CrUX data + lab data
  • Web Vitals Chrome extension — see metrics as you browse
  • Search Console — Core Web Vitals report (real user data, 28-day average)
  • Lighthouse CI — automated testing in your deployment pipeline

Set a performance budget and fail the build if you regress:

# lighthouse-ci config
ci:
  assert:
    assertions:
      first-contentful-paint: ['warn', {maxNumericValue: 2000}]
      largest-contentful-paint: ['error', {maxNumericValue: 2500}]
      cumulative-layout-shift: ['error', {maxNumericValue: 0.1}]
Enter fullscreen mode Exit fullscreen mode

The 90+ checklist

  • [ ] TTFB < 200ms (Redis FPC + CDN)
  • [ ] LCP image preloaded with fetchpriority="high"
  • [ ] Images in WebP/AVIF format with width/height attributes
  • [ ] No render-blocking scripts in <head>
  • [ ] Critical CSS inlined
  • [ ] JS bundle < 200KB initial
  • [ ] Web fonts preloaded with font-display: swap
  • [ ] CLS < 0.1 (reserved space for all dynamic elements)
  • [ ] Third-party scripts deferred until interaction

Most stores can get from 40 to 80+ by fixing TTFB and LCP alone. Getting to 90+ requires addressing all the above.


Originally published on magevanta.com

Top comments (0)