# Core Web Vitals for Shopify: a practical fix list

> LCP, INP and CLS explained in Shopify terms, with the specific theme and app changes that move each metric — ordered by how much they actually help.

- Published: 2026-08-27T05:24:36.000Z
- Author: Shopyra Team, Product & Engineering
- Category: Performance
- Tags: core web vitals, page speed, SEO
- Canonical: https://shopyra.app/blog/shopify-core-web-vitals-guide

---

Most Core Web Vitals advice for Shopify is either “install a speed app” or a wall of theory. This is the middle version: what each metric means for a storefront, and the specific changes that move it, ordered by impact.

Before you start, get real numbers. The Shopify speed score in your admin is a lab test on a synthetic device — useful for spotting regressions, useless for knowing what your customers experience. Use the Chrome UX Report data in [PageSpeed Insights](https://pagespeed.web.dev/), which reflects real visits to your store over the previous 28 days.

## LCP — Largest Contentful Paint

**What it measures:** how long until the biggest thing above the fold has painted. On almost every Shopify homepage this is the hero image. On a product page it is usually the main product photo.

**Target:** under 2.5 seconds for 75% of visits.

### Fixes, in order of payoff

**1. Stop lazy-loading your hero.** This is the most common own-goal in Shopify theming. `loading="lazy"` on the LCP image tells the browser to deprioritise the one thing the metric measures. Your hero should be `loading="eager"` with `fetchpriority="high"`. Everything below the fold should be lazy.

**2. Serve an image sized for the device.** A 2400px hero delivered to a 390px phone is roughly 30× more pixels than needed. Shopify’s `image_url` filter with a proper `srcset` and a correct `sizes` attribute solves this:

```
{% assign img = section.settings.hero_image %}
<img
  src="{{ img | image_url: width: 1200 }}"
  srcset="{{ img | image_url: width: 480 }} 480w, {{ img | image_url: width: 800 }} 800w, {{ img | image_url: width: 1200 }} 1200w, {{ img | image_url: width: 1800 }} 1800w"
  sizes="(min-width: 1024px) 100vw, 100vw"
  width="{{ img.width }}"
  height="{{ img.height }}"
  loading="eager"
  fetchpriority="high"
  alt="{{ img.alt | escape }}">
```

The `sizes` attribute is the part people get wrong. If it does not describe the real rendered width, the browser picks the wrong candidate and all the work above is wasted.

**3. Preload the hero.** One line in `theme.liquid`, inside the `{% if template == 'index' %}` branch so you are not preloading a homepage image on every page.

**4. Audit your app scripts.** Open DevTools → Network, filter to JS, sort by size. Anything over 50KB that is not your theme deserves a question. Apps you trialled and abandoned frequently leave script tags behind — check `Online Store → Themes → Edit code` for orphaned snippets.

**5. Self-host fonts or use `font-display: swap`.** A render-blocking font request from a third-party origin adds a connection setup you cannot control. If the hero text is the LCP element, this is directly on the critical path.

## INP — Interaction to Next Paint

**What it measures:** how long the page takes to visibly respond after a tap or click. It replaced First Input Delay because FID only measured the first interaction and only measured the delay, not the response.

**Target:** under 200ms for 75% of visits.

**This is the metric Shopify stores fail most often**, and it is almost always apps. Every app that adds a global click listener, every chat widget that boots on load, every review widget that re-renders the page — they all compete for the same main thread that needs to handle the tap.

### Fixes

**1. Count your apps honestly.** Open your storefront and list every third-party feature on the page. For each one, ask whether it earns its place. The median store we audit is running two apps it forgot it installed.

**2. Defer everything non-critical.** Chat widgets, review carousels, popups and analytics should not load during the initial render. Most modern apps support an idle-load or on-interaction mode; a surprising number just do not have it turned on.

**3. Break up long tasks.** If you have custom JavaScript doing work over a large collection, chunk it. Anything holding the main thread for more than 50ms is a long task and directly hurts INP.

**4. Watch for layout thrash in sticky headers.** Reading `getBoundingClientRect()` in a scroll handler and then writing a style forces synchronous layout on every frame. Batch reads and writes, or use `IntersectionObserver` instead.

## CLS — Cumulative Layout Shift

**What it measures:** how much visible content jumps around while the page loads.

**Target:** under 0.1.

CLS is the easiest of the three to fix because the causes are finite:

- **Images without dimensions.** Always set `width` and `height` (or a CSS `aspect-ratio`). The browser then reserves the box before the file arrives.
- **Web fonts swapping.** A fallback font with different metrics reflows the text when the real font lands. Use `size-adjust` and `ascent-override` in your `@font-face` to match metrics, or a tool that generates them.
- **Injected banners.** Announcement bars, cookie notices and free-shipping bars that insert themselves at the top of the DOM after paint push everything down. Reserve their height in CSS or render them server-side.
- **Embedded content.** Reviews, Instagram feeds and video embeds that expand after loading. Give them a min-height.

## What to do this week

If you only have an afternoon:

1. Run PageSpeed Insights on your homepage, a collection page and your best-selling product page. Record the field data numbers.
2. Fix `loading` and `fetchpriority` on your hero image.
3. Add `width` and `height` to every image in your theme that lacks them.
4. Uninstall one app you are not using, and check the theme code for what it left behind.
5. Re-measure in 28 days, because field data is a rolling window and will not move overnight.

That list has, in our experience, moved more stores from red to green than any speed-optimisation app ever has.

If you want to know what a small app looks like, all six of ours publish their bundle size on [their pages](/apps). Ours are under 30KB. Most are not.

---

Publisher: Shopyra (shopyra.app). Not affiliated with Shopify Inc.
Contact: hello@shopyra.app
