How to Actually Improve Your Shopify Lighthouse Score and Core Web Vitals
You ran PageSpeed Insights, got a red number in the 30s, and now you want to know how to make it green. This guide explains what that number really means, why chasing 100 is the wrong goal, and exactly which levers move Core Web Vitals on a Shopify store, in the order that gets you the most improvement per hour of effort.
Core Web Vitals are a confirmed ranking signal, so this is not vanity. A slow store ranks lower, bounces more, and converts worse, especially on mobile, which is now the majority of Shopify traffic. But the way most store owners approach the score actively wastes their time. Let us fix that first.
First, stop chasing 100
Most Shopify performance work fails for one reason: the team treats a perfect 100/100 lab score as the target. It is not achievable, or worth achieving, for a real store carrying product photography, reviews, and a few genuinely useful apps.
A healthy mobile PageSpeed score for a real, revenue-generating Shopify store sits between 60 and 80. Above 80 is excellent. For context, the top three organic results on most commercial search queries load in about 1.8 to 2.4 seconds and score above 75 on mobile. That is the bar to clear to compete, not a synthetic hundred.
So the goal is not "green everywhere." The goal is: LCP under 2.5s, INP under 200ms, CLS under 0.1, on mobile, on your money-making templates. Hit that and the score takes care of itself.
Lab data vs field data: know which one you are looking at
This trips people up constantly, so it is worth being precise.
Lab data is a simulated, single load in a controlled environment. Lighthouse (in Chrome DevTools) and the lab section of PageSpeed Insights produce it. It is repeatable and perfect for diagnosing specific causes. It does not reflect what your actual customers experienced.
Field data is aggregated from real visits. Google evaluates Core Web Vitals at the 75th percentile of real-user sessions, split across mobile and desktop. This is what feeds ranking signals. When you run a URL through PageSpeed Insights, the top of the report shows this field data, drawn from the Chrome User Experience Report (CrUX), a rolling 28-day dataset of real Chrome users, provided your store gets enough traffic to be included. PageSpeed Insights itself is a one-time check rather than a dashboard, so for tracking trends over time, Shopify's own Web Performance reports are the better tool (though that data can lag up to 36 hours).
The practical rule: field data tells you what to prioritize, lab data tells you how to fix it. If your field LCP is bad, use the Lighthouse lab diagnostics to find the render-blocking resource or oversized image causing it. Do not fix things that only look bad in lab but are fine in the field.
The three metrics, and what actually drives each one
LCP (Largest Contentful Paint) — target under 2.5s
LCP measures how long until the largest visible element finishes rendering, usually your hero image or main heading. On Shopify, this is almost always the metric to fix first, and it is almost always dominated by images.
What drives it up:
- A large, uncompressed hero image above the fold.
- The hero image lazy-loaded by mistake (it should load eagerly).
- Render-blocking CSS or JavaScript delaying the paint.
- Slow server response, though on Shopify's infrastructure this is rarely the bottleneck.
How to fix it:
- Compress the hero source before upload. You do not need to convert it to WebP or AVIF yourself; Shopify's CDN negotiates the best modern format per browser automatically when you serve through the image filters.
- Right-size the image to the space it fills. A 2000px file in a 500px slot delays everything. Request the size you need with
image_url:
{{ section.settings.hero | image_url: width: 1500 | image_tag: preload: true }}
- That
preload: truesends a preload resource hint for your LCP image, which is one of the most direct LCP wins available. Use it only on the above-the-fold hero, never site-wide. - Make sure the hero loads eagerly, not lazily.
image_taglazy-loads below-the-fold images by default, which is what you want everywhere except the hero. Custom theme work frequently breaks this split, so check it explicitly. - Defer non-critical scripts so they stop blocking the paint (see INP below).
INP (Interaction to Next Paint) — target under 200ms
INP replaced First Input Delay. It measures how quickly the page responds when someone taps or clicks. A high INP means the page looks loaded but is not yet responsive, because the browser's main thread is busy running JavaScript.
What drives it up:
- Too many third-party app scripts competing for the main thread.
- Heavy tracking tags and tag managers firing early.
- Chat widgets, heatmaps, and popup logic loading before the user can interact.
How to fix it:
- This is an app-and-script problem, so the fix is the app audit (see below).
- Defer non-critical JavaScript so it runs after the page is interactive. Any script you control in the theme should load with
defer:
<script src="{{ 'theme.js' | asset_url }}" defer="defer"></script>
- Remove tracking and marketing scripts that fire before interaction.
CLS (Cumulative Layout Shift) — target under 0.1
CLS measures how much the layout jumps around while loading. It is the one shoppers feel viscerally, the "I tried to tap the button and it moved" frustration.
What drives it up:
- Images without explicit width and height, so the browser does not reserve space.
- App or ad content injected into the layout after initial render.
- Web fonts loading late and reflowing text.
- Banners or announcement bars that appear after paint and push content down.
How to fix it:
- Set explicit width and height (or a reserved aspect-ratio container) on every image. The good news:
image_tagaddswidthandheightfor you automatically from the image's real aspect ratio, so simply using it correctly protects most of your CLS. Hand-rolled<img>tags without dimensions are the usual culprit. (See our post on image and video optimization for more on this.) - Reserve space for anything injected late, including app widgets and banners.
- Control font loading with
font_faceandfont-display: swap, and limit font families to reduce late reflow:
{% style %}
{{ settings.type_body_font | font_face: font_display: 'swap' }}
{% endstyle %}
The fix order that gives you the most improvement per hour
Do these in order. The early ones do most of the work.
1. Audit and remove apps
App sprawl is the single biggest cause of slow Shopify stores, ahead of the platform, the theme, or anything else. Nearly every app injects scripts on every page load whether or not the shopper uses that feature, and they pile onto the main thread and wreck INP.
- List every installed app in your admin.
- Remove any that are not clearly earning their keep. Uninstalling beats disabling, because stopped apps leave residual code.
- Then disable leftover embeds under Online Store > Themes > Customize > App embeds. This is the step everyone forgets. The app is gone but its embed still fires.
2. Compress and fix images
Images are roughly 27% of page weight and the number one drag on LCP. Compress the source before upload, serve through image_url / image_tag (which set dimensions and generate a srcset for you, and let the CDN handle WebP/AVIF), right-size to the display, and confirm the hero loads eagerly while everything below the fold is lazy-loaded. Done properly this can cut page weight 60 to 70%.
3. Defer non-critical third-party scripts
Tracking pixels, chat, heatmaps, and popup logic almost never need to fire before the shopper can interact. Defer them. This is the biggest single lever on INP after the app audit.
4. Address the theme
If the store carries heavy legacy theme code, the first three steps will move the number but often will not push it past 60 on their own. Themes built on Online Store 2.0 with section groups load faster than older customized themes carrying years of edits. Minify CSS and JS, remove unused sections, and keep Liquid loops tight. For a store with real performance debt, a controlled rebuild on modern architecture frequently pays back faster than fixing it piece by piece.
One theme-level lever worth knowing at the code level: Shopify's image_tag already lazy-loads below-the-fold images by default, but if your theme needs custom control over which sections count as "above the fold," the section.index property tells you a section's position on the page. A common pattern is to load eagerly for the first few sections and lazily below them:
{% if section.index > 3 %}
{{ image | image_url: width: 800 | image_tag: loading: 'lazy' }}
{% else %}
{{ image | image_url: width: 800 | image_tag }}
{% endif %}
This is exactly the kind of logic custom themes get wrong, either lazy-loading everything (including the hero, which tanks LCP) or nothing (which bloats the initial load).
A representative real-world path: a store starting at a mobile PageSpeed score of 20 reached 59 after one focused sprint, then 89 after a second pass plus a controlled theme rebuild. The jump from the 20s to the high 80s came from theme-level work, not from tweaks.
Measure the right templates
Do not judge your whole store by the homepage. Every template type (home, collection, product, cart, blog) has its own bottlenecks, and a product page loading a dozen media assets and three widgets behaves nothing like the homepage. Test each type separately, mobile first, and prioritize product and collection pages because that is where purchases happen.
Take a screenshot of your baseline before you start. You will want the before-and-after, and re-measure after each meaningful change so you know what actually moved.
The honest maintenance reality
A good score is not permanent. New apps, theme updates, and seasonal content all push the numbers back down; a store gets heavier unless someone tunes it. Install a Web Vitals monitor, set alerts for when LCP or INP regresses, and review monthly.
Everything here is doable yourself, and it is the right sequence. The catch is that getting from a red 30s score into a solid green 80s usually requires the theme-level engineering in step four, which is where the work gets genuinely technical and where most DIY efforts stall in the low 60s. If you would rather have your Core Web Vitals brought into the green and kept there to a measurable standard, that is the kind of work worth handing to someone who does it every day.
Reference
The Liquid filters and objects referenced here are documented on Shopify's developer site:
image_url: https://shopify.dev/docs/api/liquid/filters/image_urlimage_tag: https://shopify.dev/docs/api/liquid/filters/image_tagfont_face: https://shopify.dev/docs/api/liquid/filters/font_facesectionobject (section.index): https://shopify.dev/docs/api/liquid/objects/section- Performance best practices for Shopify themes: https://shopify.dev/docs/storefronts/themes/best-practices/performance