E-commerce speed optimization — Core Web Vitals explained

A slow online store loses 40% of visitors at 3 seconds and 7% conversion per extra second. Core Web Vitals are the metrics Google uses to measure real user experience. We explain each, with concrete causes and fixes you can apply today.

Is your store loading slow? See our optimization service or request a free audit.

What are Core Web Vitals

Core Web Vitals are three standardized Google metrics that measure the real user experience (NOT lab estimates). Data comes from real visitor browsers — Chrome UX Report (CrUX). Since June 2021 they are a confirmed ranking factor: green sites get a SERP advantage, red ones are penalized.

The three metrics cover three distinct dimensions: loading (LCP), interactivity (INP), and visual stability (CLS). All must be green to pass the Google threshold.

LCP — Largest Contentful Paint (loading)

Definition: time until the largest visual element in the viewport fully renders. In an online store, the dominant element is usually the hero image, main slider, or page title.

Google targets: under 2.5 seconds (green). 2.5s-4s (orange). above 4s (red).

Common LCP failure causes

  • Unoptimized hero image: 3-5 MB JPEG/PNG, no modern format (WebP/AVIF), no responsive sizes.
  • Render-blocking resources: non-critical external CSS, synchronous Google Fonts, JS in head without defer.
  • High TTFB: server responds in over 600ms. See also database optimization.
  • Client-rendered layouts: main content appears only after JS execution (SPA without SSR/SSG).

LCP fixes

  • Preload LCP image: <link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
  • Modern formats: WebP (with JPEG fallback) or AVIF. 30-70% reduction vs JPEG.
  • Responsive: srcset + sizes to serve the right image per device.
  • Inline critical CSS: above-the-fold CSS directly in <style>, rest loaded async.
  • Image CDN: Cloudflare Images, Cloudinary, imgix — on-demand resize + conversion via URL.

INP — Interaction to Next Paint (interactivity)

Definition: average latency of all user interactions (click, tap, keyboard) with the page during the visit. Replaced FID in March 2024 and is much stricter.

Google targets: under 200ms (green). 200ms-500ms (orange). above 500ms (red).

Common INP failure causes

  • Long JS tasks: loops that block the main thread over 50ms (parsing large JSON, rendering hundreds of products).
  • Third-party scripts: chats, popups, marketing pixels that listen to document clicks.
  • Heavy re-renders: React/Vue re-rendering the whole tree on every state change.
  • Body/window event handlers: listening to every mouse move and running expensive logic.

INP fixes

  • Yield to main thread: split long tasks with setTimeout, requestIdleCallback, or scheduler.yield().
  • Partytown: move third-party scripts (GA, Meta Pixel) to a Web Worker — no longer blocks main thread.
  • Debounce/throttle: on scroll, resize, input handlers.
  • List virtualization: render only visible items (react-window, vue-virtual-scroller) for lists over 100 items.
  • Memoization: useMemo, React.memo, computed properties to avoid unnecessary re-renders.

CLS — Cumulative Layout Shift (visual stability)

Definition: sum of all unexpected visual shifts during the visit. High CLS = content "jumps" under the finger, visitor hits the wrong button, frustration.

Google targets: under 0.1 (green). 0.1-0.25 (orange). above 0.25 (red).

Common CLS failure causes

  • Images without dimensions: without width + height, browser reserves no space, content jumps after load.
  • Late-appearing fonts: without font-display: swap, text re-dimensions when the font loads.
  • Banners/cookies appearing after 2s: popups, banners, newsletter modals that push content.
  • Ads and embeds without reserved space: YouTube iframes, review widgets.

CLS fixes

  • Always dimensions on img: <img width="800" height="600"> — browser reserves aspect ratio.
  • Min-height on containers: for banners, sliders, video players: aspect-ratio: 16/9.
  • Preconnect to fonts: <link rel="preconnect" href="https://fonts.gstatic.com"> + font-display: swap.
  • Reserve space for ads: fixed-size container, ad loads inside.
  • Fixed/overlay popups: don't push content, float above.

Images — the biggest opportunity

In 80% of the stores we audit, images represent 60-80% of page weight. Optimizing them alone can take LCP from 4s to 2s.

  • Format: AVIF > WebP > JPEG/PNG. <picture> with fallback: <source srcset="img.avif" type="image/avif"> + <source srcset="img.webp" type="image/webp"> + <img src="img.jpg">.
  • Compression: 80-85% quality for photos, 90% for products. Below 80% visible artifacts.
  • Lazy load: loading="lazy" on all below-the-fold images. NEVER on the LCP image — use fetchpriority="high".
  • Responsive: generate 3-5 sizes per image (400w, 800w, 1200w, 1920w), serve via srcset.
  • Host on CDN: Cloudflare Images, Cloudinary. On-demand resize via URL.

Blocking JavaScript — cut the bundle

JS bundles over 200KB gzipped are a problem. Every KB executed on the main thread delays INP and LCP.

  • Tree-shaking: import only what you use. import { debounce } from 'lodash' not import _ from 'lodash'.
  • Code-splitting: route-level chunks with React.lazy / Vue async components. Dashboard doesn't load on homepage.
  • Defer all scripts: <script defer> on everything non-critical. No JS in head without defer.
  • Audit Shopify apps: many themes have 10-20 apps loading their own JS. Uninstall unused ones.
  • Partytown for analytics: GA4, Meta Pixel, Hotjar — all in Web Worker, not main thread.

Fonts — squeezing tenths of a second

  • Max 2 families: one for headings, one for text. Each family = one more request + delay.
  • font-display: swap: show text immediately with fallback font, then swap to real font. Eliminates FOIT (flash of invisible text).
  • Preload: <link rel="preload" as="font" type="font/woff2" crossorigin> for the main font.
  • Subset: load only characters used on the site (latin-ext for RO/EN). Reduces woff2 from 100KB to 20KB.

Measurement tools

  • PageSpeed Insights (pagespeed.web.dev): combined Lab (Lighthouse) + Field (CrUX) report. Free, essential.
  • Lighthouse (Chrome DevTools): local audit on any page. Separate mobile/desktop mode.
  • WebPageTest (webpagetest.org): detailed waterfall from multiple locations. For deep audits.
  • Google Search Console — Core Web Vitals: aggregated report across all real visitors. Shows how many URLs are green/orange/red.
  • Chrome UX Report (CrUX): public data per origin (if site has enough traffic).

Concrete case — before/after

WooCommerce store, 15,000 visitors/month, 1.2% conversion. Complaint: "loads slow on mobile".

Initial diagnosis (PageSpeed mobile):

  • LCP: 6.8s (red) — 4.2MB JPEG hero image, no preload
  • INP: 480ms (orange) — 14 Shopify-style apps, 1.2MB JS
  • CLS: 0.32 (red) — images without dimensions, fonts without swap

Interventions (3 weeks):

  • Image conversion to WebP + responsive (hero from 4.2MB to 180KB)
  • Redis object cache + indexes on WooCommerce tables
  • Lazy load + dimensions on all images, fetchpriority="high" on hero
  • Defer JS, Partytown for GA, removed 6 redundant apps
  • Cloudflare CDN + on-demand image resize

Result (after 30 days):

  • LCP: 2.1s (green) — 69% improvement
  • INP: 160ms (green) — 67% improvement
  • CLS: 0.04 (green) — 87% improvement
  • Conversion: 1.9% (+58%). Organic traffic: +14% in 60 days.

Keep reading