Shopify & Performance
Core Web Vitals on Shopify: how to fix LCP, INP and CLS at the source
A hands-on guide to fixing LCP, INP and CLS on a Shopify store: the 2026 thresholds, Liquid code examples, an app audit, font and image strategy, and the right measurement tools.
Short answer
Core Web Vitals on Shopify come down to three numbers: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1, measured at the 75th percentile of real users. In most stores the root causes are, in order, a late-loading hero image (LCP), third-party app scripts (INP) and images, banners and font swaps without reserved space (CLS).
Work in this order: find the red metric in the Web Performance report in Shopify Admin, apply the Liquid and CSS fixes in this article to your theme, then remove unused apps and switch off their app embeds. Checkout is hosted by Shopify and outside your control.
What are Core Web Vitals and what are the 2026 thresholds?
Core Web Vitals are three page-experience metrics Google measures from real user data: loading speed (LCP), interaction responsiveness (INP) and visual stability (CLS). Google uses them as a page-experience ranking signal, and each metric is assessed at the 75th percentile of your visitors.
The data source is the Chrome User Experience Report (CrUX). A Lighthouse score of 95 in the lab does not mean your customers’ phones see green. Most Shopify traffic is mobile, so judge yourself on the mobile dataset.
| Metric | What it measures | Good | Needs improvement | Poor |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | Time until the largest content element is visible | ≤ 2.5 s | 2.5 – 4.0 s | > 4.0 s |
| INP (Interaction to Next Paint) | Delay in responding to taps, clicks and key presses (worst value across the visit) | ≤ 200 ms | 200 – 500 ms | > 500 ms |
| CLS (Cumulative Layout Shift) | Sum of unexpected layout shifts | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |
INP replaced FID (First Input Delay) on 12 March 2024. FID only measured the first interaction; INP reports the slowest interaction over the whole visit. That is why INP is the metric that most often goes red on Shopify stores: dozens of app scripts keep the main thread busy.
The revenue effect of speed is measured, not assumed. Google and Deloitte’s “Milliseconds Make Millions” study across 37 brands found that a 0.1-second improvement in mobile speed lifted retail conversion by 8.4%. Vodafone measured an 8% sales increase after improving LCP by 31% (web.dev case study).
What you can change on Shopify, and what you cannot
Shopify is a hosted platform. Servers, CDN and checkout are not yours to tune; theme code, images, fonts and apps are entirely yours. A realistic optimisation plan starts from that split.
| Layer | Controlled by | What you can do |
|---|---|---|
| Server response (TTFB), CDN, HTTP/2 and HTTP/3 | Shopify | Nothing; it is already in good shape |
| Checkout pages | Shopify | Limited customisation via Checkout Extensibility; performance is Shopify’s |
| Theme (Liquid, CSS, JS, sections) | You | Most of LCP, CLS and INP is solved here |
| Images and fonts | You | Size, format, priority and loading strategy |
| Apps and app embeds | You | The number one INP cause; remove and disable |
| Third-party tags (Pixel, chat, A/B testing) | You | Load timing and consent-gated loading |
Measure first: which metric, on which page type?
Use three data sources together. Relying on a single tool is the most common mistake on Shopify.
- Shopify Admin → Online Store → Themes → Web performance. Shopify’s own CrUX-based report. It shows LCP, CLS and INP from your real visitors and benchmarks them against the Shopify average. Start here.
- Google Search Console → Experience → Core Web Vitals. Tells you which URL groups (product pages, collections, home) are poor. On Shopify the problem is usually specific to a page type.
- PageSpeed Insights. Field data (CrUX) at the top, lab diagnostics below. The “LCP element” and “Avoid large layout shifts” sections show exactly which DOM element to fix.
Lab tools are not enough for INP; it needs interaction. Use the Interactions track in the Chrome DevTools Performance panel, or temporarily add the attribution build of the web-vitals library and log which script causes the delay:
<script type="module">
import {onINP, onLCP, onCLS} from 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.js';
const log = m => console.log(m.name, Math.round(m.value), m.attribution);
onINP(log); onLCP(log); onCLS(log);
</script>
LCP: prioritise the hero image from the first byte
On Shopify stores the LCP element is, nine times out of ten, the hero image on the home page, the first product image on a collection and the main product photo on a product page. Four techniques are enough.
1. Request the right size in a modern format
Shopify’s CDN converts images to WebP/AVIF automatically, but only sends the right size when you pass proper width and sizes values. Sending a 4,000-pixel original to a 390-pixel phone is the most common LCP mistake.
{{ section.settings.image
| image_url: width: 1600
| image_tag:
widths: '480, 768, 1024, 1366, 1600',
sizes: '100vw',
loading: 'eager',
fetchpriority: 'high',
preload: true,
alt: section.settings.image.alt }}
preload: true makes Shopify’s image_tag filter emit a <link rel="preload">; fetchpriority="high" tells the browser to download this image ahead of other resources. These two lines alone can cut LCP by 0.5–1.5 seconds.
2. Never lazy-load above-the-fold images
Dawn-based themes ship with loading="lazy" on every image. Set the hero and the first product cards to eager. A lazy-loaded LCP image does not even start downloading until the browser has computed layout.
3. Drop the slider
Auto-rotating hero sliders delay LCP, create CLS, and there are very few measured cases where they help conversion. One static hero, a clear headline and a single CTA is the best-performing pattern on Shopify.
4. Get fonts and CSS out of the way
Add font_display: 'swap' to Shopify’s font_face filter and preload the font file. Avoid third-party font services (including Google Fonts); Shopify’s own font library is served from its CDN with no extra DNS/TLS cost.
{{ settings.type_header_font | font_face: font_display: 'swap' }}
<link rel="preload" as="font" type="font/woff2" crossorigin
href="{{ settings.type_header_font | font_url }}">
INP: audit your app scripts
INP problems are almost always JavaScript, and on Shopify most JavaScript comes from apps you did not write. Each installed app adds one to three scripts on average, some more than ten. They create long tasks (>50 ms) on the main thread, so when a customer taps “Add to cart” the browser is not idle.
Audit steps
- List every app and mark the ones actually used in the last 90 days. Remove the rest. Removing is not enough: go to Online Store → Themes → Customize → App embeds and switch off leftover embeds; some apps leave code in
theme.liquidafter uninstall. - Clean
theme.liquidand the layout files. Search for<script>lines left by old apps: anything referencingcdn.shopify.com/extensionsor scripts outside{{ content_for_header }}. - Measure and compare. DevTools Performance → record → tap “Add to cart” → look at the Interactions track and the long tasks. The file name tells you which script is responsible.
- Defer what remains. Use
deferon your own theme JS, push non-urgent work intorequestIdleCallback, and make scroll and resize listeners{ passive: true }.
// Break up long tasks: do the heavy work after the interaction
button.addEventListener('click', () => {
updateCartUI(); // immediate visible response
requestIdleCallback(() => { // heavy work when idle
trackAnalytics(); rebuildRecommendations();
});
});
Practical threshold: among the stores we have audited, no Shopify store with more than 15 active apps passes the mobile INP “good” threshold. The target is that no app without a direct contribution to sales loads a script on the storefront.
CLS: reserve space for everything
Layout shift has three causes: images without declared dimensions, banners and widgets injected after load, and font swapping. All three are solved by the same principle: reserve the space in advance.
- Images: the
image_tagfilter addswidthandheightautomatically. Do not omit them on hand-written<img>tags, or use CSSaspect-ratio. - Announcement bars and app widgets: everything added to the DOM later (free-shipping bar, chat bubble, pop-up) pushes the content below it. Give the container a
min-height; keep pop-ups out of the flow withposition: fixed. - Font swap:
font-display: swapfixes visibility, but fonts with different metrics still shift text. Match the fallback withsize-adjustandascent-override:
@font-face {
font-family: "Fallback-Heading";
src: local("Arial");
size-adjust: 104%;
ascent-override: 92%;
descent-override: 22%;
}
h1, h2 { font-family: "Brand-Font", "Fallback-Heading", sans-serif; }
On product pages, the gallery re-rendering when a variant is selected also produces CLS. Give the gallery container a fixed aspect ratio and upload variant images in the same ratio.
Shopify-specific checklist
| Check | Metric | Where |
|---|---|---|
| Online Store 2.0 theme (Dawn or a derivative) | All | Themes |
Hero image: loading: 'eager', fetchpriority: 'high', preload: true | LCP | sections/image-banner.liquid |
| First 2–4 product cards eager, the rest lazy | LCP | sections/main-collection-product-grid.liquid |
widths + sizes on every image | LCP, CLS | snippets/card-product.liquid |
| Unused apps removed, app embeds switched off | INP | Apps, Theme → App embeds |
| No orphan scripts in theme.liquid | INP | layout/theme.liquid |
Shopify fonts with font_display: 'swap' + preload | LCP, CLS | snippets or layout |
| min-height on announcement bar and widgets | CLS | CSS |
| Pixel, chat and A/B scripts loaded conditionally on consent | INP | Customer events / GTM |
No background video on the home page, or preload="none" + poster | LCP | sections |
How to verify the ranking and revenue effect
CrUX uses a 28-day rolling window, so it takes 2–4 weeks for changes to be fully reflected. Watch two things in the meantime: the count of “good” URLs rising in Search Console, and mobile conversion rate in Shopify Analytics. Plan speed work alongside ad campaigns; buying traffic for a store with poor INP throws part of the budget away.
Core Web Vitals are only one part of technical SEO. For structured data and conversion measurement, read our Schema.org JSON-LD guide for e-commerce and GA4 Consent Mode v2 and Meta CAPI setup.
Key takeaways
- Thresholds: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1, assessed at the 75th percentile of real users, on mobile.
- On Shopify the server and checkout belong to Shopify; nearly all of LCP, INP and CLS is solved in the theme, image, font and app layer.
- For LCP, use
preload: true,fetchpriority: 'high'andloading: 'eager'on the hero image and drop the slider. - App scripts are the number one INP cause: remove unused apps, switch off app embeds, clean theme.liquid.
- For CLS, give every image dimensions, every widget a min-height and the fallback font a size-adjust.
- Measure with the Shopify Web Performance report, Search Console and PageSpeed Insights together; CrUX updates on a 28-day window.
Frequently asked questions
What score is good enough for Core Web Vitals on Shopify?
It is not a score. Each of the three metrics must be within the “good” threshold: LCP at or under 2.5 seconds, INP at or under 200 milliseconds, CLS at or under 0.1. Google assesses these at the 75th percentile of real visitors, separately for mobile and desktop. Lighthouse’s 0–100 score is a lab estimate and is not used for ranking.
Do Shopify apps really slow down the store?
Yes, INP in particular. Every app adds JavaScript to the storefront; these scripts occupy the main thread and delay the response to taps. Uninstalling is not enough: switch off leftover embeds under Theme → Customize → App embeds and delete script lines left in theme.liquid.
What is the single fastest LCP win on Shopify?
Outputting the hero image through the image_tag filter with preload: true, fetchpriority: 'high' and loading: 'eager'. The browser then starts downloading the largest image as soon as it reads the HTML and ahead of other resources; a 0.5–1.5 second LCP improvement is typical in practice.
Can I improve the speed of the Shopify checkout?
No. Checkout is hosted and optimised by Shopify; your theme code does not run there. Since checkout.liquid was retired, customisation happens only through Checkout Extensibility. Focus Core Web Vitals work on the home, collection and product pages.
What is INP and how does it differ from FID?
INP (Interaction to Next Paint) is the time until the screen responds to the slowest tap, click or key press across the whole visit. FID measured only the input delay of the first interaction. INP replaced FID in Core Web Vitals on 12 March 2024 and is the metric that most often fails on Shopify stores.
When do speed improvements show up in rankings?
Google’s field data (CrUX) uses a 28-day rolling window, so full reflection in the Search Console Core Web Vitals report takes 2–4 weeks. The ranking effect is small but measurable; the real win is mobile conversion rate, which you can see in Shopify Analytics the same week.
Sources
- Google web.dev – Web Vitals (thresholds and the 75th percentile rule)
- Google web.dev – Interaction to Next Paint (INP)
- Google Search Central – Core Web Vitals and page experience
- Shopify Help Center – Web performance report
- Shopify.dev – Theme performance best practices
- Shopify.dev – image_tag filter (preload, fetchpriority, widths, sizes)
- Deloitte & Google – Milliseconds Make Millions (2020)
- web.dev – Vodafone: 31% LCP improvement, 8% more sales
- GoogleChrome/web-vitals library