Fixing Core Web Vitals in Shopify
On Shopify you cannot tune the server or the checkout, but you still control most of what decides Core Web Vitals: the theme, the apps that inject code into every page, how images are written in Liquid, and the tracking you add.
Shopify stores tend to fail in a recognisable pattern: a hero image that loads late on the home page, collection pages slowed by filters and review widgets, and product pages that respond sluggishly because of app scripts. This guide works through those Shopify-specific causes in the order that usually gives the biggest improvement, with Liquid you can adapt to your own theme.
What you can and cannot change on Shopify
| Area | Who controls it | What that means for Core Web Vitals |
|---|---|---|
| Hosting, CDN and HTTPS | Shopify | Server and image delivery are handled for you; you cannot change caching rules or server location. |
| Checkout | Shopify | Checkout performance is largely outside theme control, so focus on the storefront pages. |
| Theme code (Liquid, CSS, JavaScript) | You or your developer | Most LCP and CLS fixes live here. |
| Installed apps | App developers, switched on or off by you | Usually the biggest source of extra JavaScript and poor INP. |
| Tracking and marketing tags | You | Can be moved, delayed or removed. |
| Images and video | You | Shopify resizes and converts formats, but the theme decides which size is requested and when. |
Shopify's CDN already serves images in modern formats such as WebP or AVIF to browsers that support them, and can resize them on request. That removes one common job. It does not stop a theme asking for an image that is far too wide, lazy-loading the hero, or loading ten app scripts before the page can respond.
The storefront pages that matter most are usually the home page, collection pages, product pages and the cart. Each is a separate template, so a fix to the product template improves every product page at once.
Finding where your store is slow
- Field data first. Run one example of each key template through the Core Web Vitals checker and record mobile LCP, INP and CLS. Open the Core Web Vitals report in Search Console to see which URL groups fail. Shopify also shows a web performance report in the admin, based on real visits to your store, which is useful for spotting when a change made things worse.
- Script inventory. Open a product page in Chrome, go to DevTools, Network, filter
by JS and reload. Sort by domain. Anything not from your own store domain or
cdn.shopify.comis usually an app or a tag. Note the size and the number of requests for each. - Main-thread cost. Record a page load in the Performance panel with CPU throttling on, then use the Bottom-Up tab grouped by domain to see which app scripts use the most processing time.
- Interactions. Record yourself choosing a variant, adding to the cart and opening the cart drawer. Long tasks during those clicks explain a poor INP.
Apps: the usual main cause
Most Shopify apps that change the storefront add JavaScript and often CSS to every page, even pages where the app does nothing. Reviews, upsells, pop-ups, chat, loyalty schemes, currency switchers, wish lists and "speed booster" apps all compete for the same main thread.
App embeds
Apps built for Online Store 2.0 themes add their code through app embeds and app blocks. In the theme editor, open App embeds and switch off any embed you are not actively using. An embed that is switched off stops loading, even while the app stays installed.
Leftover code from removed apps
Older apps, and apps installed on older themes, often edited theme files directly or added scripts
through the older script tag mechanism. Uninstalling the app does not always remove that code. Search
the theme code for the app's name or domain in layout/theme.liquid, in the snippets
folder and in section files. Remove references to apps you no longer have, after checking with the developer
or testing on a duplicate theme.
Deciding what stays
- Remove apps that nobody can explain the purpose of, or that duplicate a feature your theme already has, such as a mega menu or product tabs.
- For each app you keep, check its settings for an option to load only on the templates where it is used.
- Prefer apps that render with app blocks in sections over apps that inject widgets with JavaScript after the page loads, because block-based output is part of the page and is less likely to cause layout shift.
- Be careful with speed optimisation apps. Some help, but some add their own scripts or lazy-load every image, including the hero, which harms LCP.
Hero images and fonts in Liquid
Many themes lazy-load every image, including the first one on the page. The fix is to load the first
section's image eagerly with high priority, and lazy-load the rest. Liquid's section.index
tells you where a section appears in the template, and the image_tag filter accepts HTML
attributes as parameters.
{%- if section.index == 1 -%}
{{ section.settings.image
| image_url: width: 1800
| image_tag:
widths: '600, 900, 1200, 1800',
sizes: '100vw',
loading: 'eager',
fetchpriority: 'high',
alt: section.settings.image.alt }}
{%- else -%}
{{ section.settings.image
| image_url: width: 1800
| image_tag:
widths: '600, 900, 1200, 1800',
sizes: '100vw',
loading: 'lazy',
alt: section.settings.image.alt }}
{%- endif -%}
image_tag writes the srcset and the width and height attributes for you,
so small screens get small files and space is reserved for the image. Adjust sizes to match
how wide the image really is at each screen size; 100vw is only right for full-width banners.
On product pages the LCP element is usually the first product image. Apply the same eager, high-priority treatment to the first media item in the product gallery and lazy-load the thumbnails and later images.
Fonts
{{ settings.type_header_font | font_url | preload_tag: as: 'font' }}
{% style %}
{{ settings.type_header_font | font_face: font_display: 'swap' }}
{{ settings.type_body_font | font_face: font_display: 'swap' }}
{% endstyle %}
Preload only the heading font used in the first screen, and keep font_display: 'swap'
so text is visible straight away. The setting names above follow Shopify's free themes; yours may differ.
Tracking, pixels and consent
Analytics, advertising and marketing pixels are often pasted into theme.liquid over the
years, sometimes several copies of the same one. Shopify's customer events feature lets you add custom
pixels that run in a sandbox separate from the theme's page, and many marketing apps now connect through
it. Moving tracking there, and removing the hard-coded copies from the theme, reduces the scripts competing
with the storefront.
- Search
theme.liquidforgtag,fbq,ttqand other pixel names, and list every copy. - Check which of those are already sent through a sales channel app or customer events, and remove the duplicates from the theme.
- Use Shopify's consent settings so marketing and analytics pixels that need consent under UK GDPR and PECR do not run until the visitor has agreed. This is not legal advice, but it also means those scripts do not load for visitors who decline.
INP on Shopify: carts, variants and filters
Poor INP on Shopify usually comes from three interactions:
- Choosing a variant. Themes and apps may update the price, image, stock message, URL and several widgets in one long task. Update the visible price and button first, then defer the rest.
- Adding to the cart. Opening a cart drawer, refreshing the cart count and running upsell and analytics code all in one click creates a long task. Show the drawer opening immediately and let app code run after the next paint.
- Collection filters. Filters that rebuild the whole product grid on every tick of a checkbox are costly. Debounce the request so it waits until the visitor pauses, and use the Section Rendering API to fetch only the updated grid.
// Give the visitor feedback now, run the heavy work after the next frame
addButton.addEventListener('click', () => {
addButton.setAttribute('aria-busy', 'true');
cartDrawer.classList.add('is-open');
requestAnimationFrame(() => setTimeout(() => addToCart(form), 0));
});
The general techniques for splitting up this kind of work are in the INP guide.
The fix, step by step
Step 1
Measure the four key templates
Pick one URL each for the home page, a busy collection, a best-selling product and the cart, and run them through the Core Web Vitals checker on mobile. Record LCP, INP and CLS from field data and the lab diagnostics for each. Check which URL groups fail in the Search Console report, and note the figures in Shopify's web performance report so you can compare after each change.
Step 2
Work on a duplicate theme
In the theme library, duplicate your live theme and make every change on the copy. Preview the copy to test each change with lab tools before publishing, and keep the original so you can switch back in seconds if something breaks. If you are still on an older vintage theme without sections on every page, consider moving to a current Online Store 2.0 theme first, as it makes later fixes far easier.
Step 3
Audit app embeds and leftover app code
List every installed app with its purpose, then switch off unused embeds under App embeds
in the theme editor and uninstall apps nobody needs. Search theme.liquid, snippets and sections
for the names and domains of apps you have removed, and delete their leftover code on the duplicate theme.
Recheck the Network panel afterwards to confirm those scripts no longer load on any template.
Step 4
Fix the hero image in Liquid
In
the section that renders first on the home page and in the product gallery's first image, use image_tag
with loading: 'eager' and fetchpriority: 'high', a realistic list of widths
and an accurate sizes value. Keep lazy loading for everything further down. Check that no
app or speed tool is adding a lazy attribute back, and confirm the change in the page source rather than
the editor.
Step 5
Move tracking to customer events
Search
the theme for hard-coded tracking such as gtag, fbq and other pixel snippets,
and list any duplicates. Where the platform's app or a custom pixel in customer events already sends
the same data, remove the theme copy. Set marketing and analytics pixels to respect the visitor's consent
choice, then place a test add-to-cart to confirm events still reach each platform correctly.
Step 6
Reserve space for app widgets and banners
Record a load of each template in the Performance panel and look for layout shifts caused
by review stars, announcement bars, payment badges, upsell blocks and pop-ups appearing late. Where possible
replace injected widgets with the app's theme block. Otherwise give the container a fixed min-height
matching the widget, and show promotional bars as overlays rather than pushing the page down.
Step 7
Speed up cart, variant and filter interactions
Record yourself choosing a variant, adding to the cart and ticking a collection filter, and find the long tasks in each. Show immediate visual feedback first, then run price updates, cart refreshes and app code after the next paint. Debounce filter changes and fetch only the updated section with the Section Rendering API. Test on a throttled mid-range phone profile, not just a desktop computer.
Step 8
Publish and watch the field data
When the duplicate theme tests well and orders, discounts, tracking and apps all work, publish it at a quiet time. Recheck the four templates in the lab, then watch Shopify's web performance report over the next days and the Core Web Vitals checker and Search Console over about four weeks, because field data is a rolling 28-day window. Start validation in Search Console once the fix is live.
Keeping the store fast
- Test before installing. Before switching a new app on for the live theme, add its embeds or blocks to a duplicate theme where the app allows it, and measure the product page before and after.
- Review apps quarterly. Apps accumulate. Remove anything unused and check that uninstalled apps have not left code behind.
- Protect the first section. If editors can reorder sections, make sure whichever
section renders first uses eager loading, which the
section.indexapproach does automatically. - Watch the admin report. Look at the web performance report after theme updates, new apps and seasonal campaigns, which often add banners and pop-ups.
For platform-specific detail on individual metrics, see layout shift in Shopify, Shopify image optimisation and JavaScript issues in Shopify.
1. Why is my Shopify store failing Core Web Vitals?
On most stores the cause is a combination of app scripts loading on every page, a hero or first product image that is lazy-loaded or oversized, tracking pixels added to the theme several times, and widgets that appear late and push content down. The Shopify platform itself handles hosting and image delivery well. Start by listing your apps and the scripts each one loads, then fix the first image on the home and product templates, which together usually account for most of the problem.
2. Do Shopify apps slow down my store even when I do not use them?
They can. Many apps load their scripts on every storefront page, whether or not the page uses the app. Switching off an app embed in the theme editor stops that embed loading, and uninstalling an app removes its app embeds, but apps that edited theme files directly or were installed on older themes can leave code behind. Search your theme files for the names and domains of removed apps and delete any leftover references after testing on a duplicate theme.
3. Can I change Shopify hosting or caching to improve speed?
No. Shopify runs the servers, the CDN and caching for all stores, so you cannot move hosting, add server caching rules or choose a server location. You can still make pages faster to render by avoiding Liquid that loops over very large collections, keeping templates lean and reducing app code. Most Core Web Vitals problems on Shopify come from the front end, meaning theme code, images, apps and tracking, which are all within your control.
4. Should I install a speed optimisation app?
Be cautious. Some of these apps do useful things, such as delaying non-essential scripts, but they add code of their own, and some lazy-load every image including the hero, which makes LCP worse. Others improve a lab score by delaying scripts in a way that makes the first real interaction slower. Before relying on one, remove apps you do not need and fix the hero image yourself, then measure whether the speed app still makes a real difference in field data.
5. Does changing to a newer Shopify theme help Core Web Vitals?
Often, yes, especially if you are on an older vintage theme. Current Online Store 2.0 themes support app blocks and app embeds, which make apps easier to control, and Shopify's own free themes are built with performance in mind. A new theme will not fix slow apps or oversized images on its own, though. Test the new theme on a duplicate with your real apps and content, and compare the key templates before publishing it.
6. How do I make my Shopify hero image load faster?
Make sure it is not lazy-loaded, give it high fetch priority, and request a sensible size. In Liquid, use the image_tag filter with loading set to eager and fetchpriority set to high for the first section on the page, and lazy for later sections, with a list of widths and an accurate sizes value. Shopify serves modern formats automatically. Avoid sliders for the hero where you can, because slider scripts often hide the first image until they have loaded.
7. Why is INP poor on my Shopify product pages?
Product pages carry the most app code: reviews, upsells, bundles, wish lists and tracking, all reacting to the same clicks. Choosing a variant or adding to the cart can trigger a long chain of scripts before the page updates. Record those interactions in Chrome DevTools to see which scripts run, remove or switch off the ones you do not need, and change the theme code so the button and cart drawer respond first and the heavier work runs just after.
The Core Web Vitals checker shows real-user field data from the Chrome UX Report and a Lighthouse lab test side by side for any store URL, breaks down each metric and points to the assets and code behind every failure.
Open Core Web Vitals Fixes โ