Most slow Shopify stores aren't slow because of Shopify. They're slow because of what's been added on top: a dozen apps, a theme patched by three different freelancers, an oversized hero image, and a chat widget that loads before the product photo does. This checklist is how we'd approach Shopify speed optimization if we were handed your store tomorrow, with the biggest wins first and honest notes on what each fix costs you.
What Shopify Core Web Vitals actually measure
Google's Core Web Vitals are three metrics, each assessed at the 75th percentile of real visits. Roughly three out of four page loads need to hit the threshold, not just your best ones.
- LCP (Largest Contentful Paint): how long until the largest visible element, usually the hero or main product image, renders. Good is 2.5 seconds or less.
- INP (Interaction to Next Paint): how quickly the page visibly responds after a tap, click, or key press, across the whole visit. Good is 200 milliseconds or less.
- CLS (Cumulative Layout Shift): how much content jumps around while the page loads. Good is 0.1 or less.
INP replaced First Input Delay (FID) as a Core Web Vital in March 2024, and it's a much harder bar. FID only measured the delay before the browser started handling the first interaction. INP looks at the responsiveness of interactions throughout the visit, including how long it takes for the screen to actually update. A store that passed FID easily can fail INP on a mid-range Android phone.
On a typical Shopify store, LCP and INP are the ones that fail. CLS is usually the quickest fix.
Measure with field data first, Lighthouse second
Before you change anything, get a baseline from the right source. There are two kinds of performance data, and mixing them up is the most common reason speed work goes in circles.
- Field data comes from real Chrome users visiting your store, collected in the Chrome UX Report (CrUX). This is what Google uses to assess Core Web Vitals. You'll find it at the top of PageSpeed Insights and in the Core Web Vitals report in Google Search Console. Search Console groups similar URLs, so you can see which template is failing.
- Lab data is a single simulated page load: Lighthouse, the lower half of PageSpeed Insights, or Chrome DevTools. It's great for debugging, but it's one synthetic visit. Lighthouse also can't measure INP, because nobody is interacting with the page. It reports Total Blocking Time as a rough proxy.
Shopify's admin also has a web performance report based on real visitor data. Its location and contents have changed over time, so check Shopify's current docs.
Three practical notes:
- CrUX reports a rolling 28-day window, so fixes you ship today won't fully show up in PageSpeed Insights or Search Console for about four weeks.
- Lower-traffic stores may not have enough CrUX data for individual URLs. Then you lean more on lab tests and Shopify's own report.
- A Lighthouse score of 100 is not the goal. A lab score in the 60s with passing field data beats a lab score of 95 with failing real-user INP.
The Shopify page speed checklist, in priority order
- Audit apps and third-party scripts, and remove code left behind by apps you've uninstalled.
- Trim theme code: render-blocking scripts, unused libraries, and heavy Liquid.
- Fix the LCP element on your homepage, collection, and product templates.
- Load fewer font files, and stop fonts from blocking text.
- Break up main-thread JavaScript, and delay review, chat, and popup widgets.
- Reserve space for everything that loads late, to eliminate layout shift.
- Re-measure with field data after 28 days, then repeat.
1. Audit Shopify apps and third-party scripts first
On a typical Shopify store, apps and third-party tags are the biggest drag on page speed. Each one can add JavaScript, CSS, and requests to another domain, sometimes including its own copy of a library your theme already loads.
Start with an inventory:
- In the theme editor, open App embeds and switch off anything you don't actively use.
- Search your theme code, especially
theme.liquidand the snippets folder, for script tags and snippets from apps you've already uninstalled. Older apps that injected code into the theme often don't clean it up, so those scripts keep loading. - Open a product page in Chrome DevTools, look at the Network panel, and filter for third-party domains. For each one, ask: who owns this, and what revenue does it support?
- Consolidate overlapping tools. Two review apps, an upsell app, and a bundle app that all do similar things will each ship their own JavaScript.
For tracking pixels, see whether they can move to Shopify's Customer Events (web pixels), which run in a sandbox instead of directly in your theme. Vendor support varies, so confirm your conversion data still lines up before you remove the old tags.
The hard part is organizational, not technical. Every app was installed for a reason. Removing apps is the fastest speed win available, but it needs someone with the authority to say no.
2. Clean up your Shopify theme code
Themes pick up weight over years of one-off customizations. The usual suspects:
- Render-blocking scripts. A script tag in the
<head>withoutdeferorasyncblocks rendering until it downloads and runs. - Libraries loaded for one feature. Loading jQuery, a carousel library, and an animation library on every page for one homepage slider is a common pattern. Shopify's Dawn reference theme is built without jQuery, which shows how far you can get without it.
- Global bundles. Load section-specific CSS and JavaScript only on pages where that section appears, rather than shipping everything everywhere.
- Heavy Liquid. Loops over large collections to build menus or filters slow server response (Time to First Byte), which delays everything else.
If your theme is several years old, heavily patched, and still on a legacy (pre-Online Store 2.0) architecture, cleanup can end up costing more than a rebuild. Ask that question honestly before you spend weeks optimizing code you'd rather replace.
3. Fix your LCP element (usually the hero or product image)
On Shopify, the LCP element is almost always an image: the homepage hero, a collection banner, or the main product photo. PageSpeed Insights diagnostics will tell you which one. Then:
- Never lazy-load it.
loading="lazy"on the LCP image is one of the most common Shopify page speed mistakes. - Give it priority.
fetchpriority="high"tells the browser to fetch that image ahead of the others. - Serve the right size. Use Shopify's
image_urlandimage_tagfilters with explicit widths and asizesvalue so phones don't download a desktop-sized banner. Shopify's CDN already serves modern formats like WebP to browsers that support them. - Drop the hero slider. Carousels delay LCP while their JavaScript initializes, add main-thread work that hurts INP, and often cause layout shift. A single static hero is faster almost every time.
- Be careful with hero video. If you keep an autoplaying background video, give it a lightweight poster image to paint first.
In Liquid, a hero image set up this way looks something like {{ section.settings.image | image_url: width: 1600 | image_tag: loading: 'eager', fetchpriority: 'high', widths: '600, 900, 1200, 1600', sizes: '100vw' }}. Match the widths and sizes to your layout.
4. Load fewer fonts, and load them smarter
If your LCP element is a large headline rather than an image, fonts are your LCP problem.
- Limit weights and styles. Every weight is a separate file. Regular, one bold, and maybe one italic covers most brands.
- Use
font-display: swapso text appears immediately in a fallback font. For fonts from Shopify's font library, thefont_facefilter accepts it directly:{{ settings.type_body_font | font_face: font_display: 'swap' }}. - Avoid extra domains. A third-party font service adds another connection before text can render; Shopify's font library or self-hosted files avoid that.
- Preload only what's above the fold. One or two critical files, not the whole family.
The tradeoff: swapping can cause a visible flash and a small layout shift when the web font arrives. Pick a fallback with similar proportions, or tune it with CSS size-adjust, to keep the swap subtle.
5. Fix INP: main-thread JavaScript and heavy widgets
INP fails when the browser's main thread is busy at the moment a shopper taps something: opening the menu, picking a variant, adding to cart. The tap waits behind running JavaScript, and the page feels frozen.
- Delay non-essential widgets. Review widgets, live chat, and email-capture popups are the usual offenders. A chat widget can start as a lightweight placeholder button that loads the real script only when clicked. Reviews can load as the shopper scrolls toward them.
- Make variant pickers and add-to-cart feel instant. Update what the shopper sees first (the selected state, a loading indicator on the button), then do the heavier work like fetching updated section HTML or firing analytics events after the browser has painted.
- Break up long tasks. Any chunk of JavaScript that runs longer than 50 milliseconds blocks input. Split the work and yield back to the main thread between chunks, using
setTimeoutorscheduler.yield()where the browser supports it. - Find the slow handler. Record a slow interaction in the DevTools Performance panel to see which script handled it and for how long.
INP is the hardest of the three to fix from the outside, because the culprit is often code you didn't write and can't edit.
6. Eliminate layout shift (CLS) on Shopify
- Set dimensions on media. Every image and video needs
widthandheightattributes or a CSSaspect-ratioso the browser reserves space. Shopify'simage_tagfilter adds width and height for you; hand-written<img>tags often don't. - Reserve space for app-injected content. Star ratings under the product title, buy-now-pay-later messaging near the price, and upsell blocks often pop in after load. Give their containers a fixed minimum height.
- Don't push content down after load. Render announcement bars in Liquid so they're in the initial HTML, and show cookie banners as overlays rather than inserting them above the page.
- Animate with
transform, not properties liketop,height, ormarginthat move surrounding content.
7. Confirm the fixes in field data, then keep them
After shipping, give it time. Once your changes are live, use Validate fix in the Search Console Core Web Vitals report. Validation tracks real-user data over roughly four weeks, so plan for that lag.
Speed also isn't a one-time project. Every new app, campaign script, or theme edit can quietly undo it. Ask "what does this do to page speed?" before anything gets installed, and check field data monthly.
Speed rarely works alone, either. In our Shopify Plus rebuild case study, average page load came in at 1.4 seconds and mobile checkout abandonment fell 52%. Performance was one part of that rebuild, alongside checkout UX work like the fixes in our guide to reducing mobile checkout abandonment.
Want a second opinion on your store's speed?
If your Core Web Vitals report is red and you can't tell whether apps, theme code, or something else is to blame, we can help. A speed audit from us starts with your field data, pins down what's actually causing the failures, and gives you a prioritized fix list you can hand to your own team or have us handle. You can read more about our Shopify and e-commerce development work, or get in touch to talk through your store.
