Fixing Poor LCP on Shopify Starts With a Setting Your Theme Already Got Wrong
Most "shopify poor lcp" advice repeats the same three lines — compress images, use WebP, lazy-load everything. The actual cause on most Online Store 2.0 themes is narrower: Dawn, Craft, and Sense apply loading="lazy" to image banner sections without checking whether that section renders first, which quietly lazy-loads the exact image Google is timing.
A poor Shopify LCP score is almost always one of two things: your theme is lazy-loading the exact image Google is using to time Largest Contentful Paint, or a CSS transition class is hiding that image from the browser's paint clock even after it has downloaded. Fix whichever one applies and the score moves immediately — no amount of generic image compression touches either cause.
Search "shopify poor lcp" or "reduce lcp shopify" and nearly everything that ranks gives you the same three lines: compress your images, switch to WebP, lazy-load everything below the fold. That advice isn't wrong, but it's also not what's actually wrong with most stores, because it assumes lazy-loading is something you control section by section. On Shopify's own Online Store 2.0 themes, it often isn't.
What LCP is actually timing
Largest Contentful Paint marks the moment the largest visible element in the viewport finishes rendering — on a typical Shopify homepage or product page, that's the hero image or the first product photo. Google's own guidance puts a "good" score at 2.5 seconds or less, measured against real visitor data (field data, via the Chrome UX Report) rather than a single lab test. That distinction matters here only in that it's the reason your PageSpeed Insights score and your Search Console field data can disagree for weeks after a fix ships — we cover that gap on its own in why your Shopify speed test score changes depending on which tool you run. This post is about the fix itself, not the reporting lag.
The default your theme already got wrong
Dawn and the other current Online Store 2.0 themes (Craft, Sense) apply loading="lazy" to image-based sections — including image banners and hero-style sections — as a blanket default. What they don't do is check whether that particular section is the first thing rendered on the page. A merchant who drags an image banner section to the top of the homepage inherits lazy-loading on the one image that most needs to load immediately, because the section template has no awareness of where it ends up in the page.
This isn't a rare misconfiguration. It's the default behavior of a stock theme, and it's specific enough that Shopify's own community forum has multiple threads from merchants asking why removing lazy-load from "the first image of the slideshow only" isn't exposed as a setting anywhere in the theme editor — because it isn't. The fix has to happen in the section's Liquid file, not in the admin.
If you're comfortable editing theme code, override the default for the first instance of a section using its index rather than turning lazy-loading off globally, which would just trade an LCP problem for a bandwidth problem further down the page:
{% liquid
assign is_first_section = false
if section.index == 1
assign is_first_section = true
endif
%}
{{ section.settings.image
| image_url: width: 1600
| image_tag:
widths: '480,768,1200,1600',
sizes: '100vw',
loading: is_first_section ? 'eager' : 'lazy',
fetchpriority: is_first_section ? 'high' : 'auto',
alt: section.settings.image.alt
}}
If you're not editing theme code directly, the same fact is still useful diagnostically: before touching image compression settings or installing a speed app, check whether the section rendering above the fold has a lazy-loading toggle in its block settings, and whether that toggle is actually set to load eagerly. Some app-installed hero sections expose this as a checkbox; Dawn's native sections mostly don't.
Confirm which element Google is actually timing
Don't guess. Open Chrome DevTools, go to the Performance panel, record a page load, and look at the LCP marker in the timeline — it names the exact DOM element. PageSpeed Insights does the same thing under "Diagnostics → Largest Contentful Paint element." On most Shopify storefronts it's one of three things: the hero image, the first product image in a grid, or — less often — a large heading rendered before a slow web font loads. The fix below is for the first two. If your LCP element is text, the cause is font loading, not images, and no amount of image work will move the score.
fetchpriority and preload, applied narrowly
Google's own guidance on this is direct: never lazy-load your LCP image, and set fetchpriority="high" on the image likely to be it. The same guidance warns that setting high priority on more than one or two images makes the hint useless — the browser needs one clear signal, not several competing ones. Applied to a Shopify hero section with Liquid's image_tag filter:
{{ section.settings.hero_image
| image_url: width: 1800
| image_tag:
widths: '480,768,1200,1600,1800',
sizes: '100vw',
width: 1800,
height: 900,
loading: 'eager',
fetchpriority: 'high',
alt: section.settings.hero_heading
}}
If the image is only referenced from CSS — a background-image on a hero div rather than a real <img> — the browser's preload scanner can't discover it early, because it isn't in the initial HTML markup. That's the one case where a manual preload hint earns its keep:
<link rel="preload" as="image" fetchpriority="high"
href="{{ section.settings.hero_image | image_url: width: 1800 }}">
The better fix, where the design allows it, is to stop using a CSS background-image for the LCP element entirely and render it as a real <img> with object-fit: cover — the browser finds it in the markup without any preload hint at all.
The trap that image optimization can't fix: a hidden LCP element
Shopify's own performance engineering blog documented a case worth knowing by name, because the symptom looks identical to a slow image even when the image itself loads fine. A merchant's theme applied a reveal class to the product image that set opacity: 0 until a fade-in script ran — and that script lived inside theme.js, which didn't execute until roughly 12 seconds into page load. The image itself downloaded around the 5-second mark. The gap between "downloaded" and "actually visible" was the entire LCP problem: removing the reveal attribute cut LCP from 13 seconds to 7 in that case, without touching a single image file.
If your image compression, lazy-loading fix, and preload hints are all correct and LCP is still bad, check for exactly this pattern: any class or attribute on the LCP element that sets it invisible until a script runs. Search your theme's CSS for opacity: 0 rules paired with a transition, and check what triggers the class removal — if it's tied to a script bundled with everything else in theme.js rather than loaded independently, the image is finished loading long before the browser is allowed to count it as painted.
A diagnostic order, not a checklist
Work through these in sequence rather than applying all of them at once — each step tells you whether the next one is necessary:
- Identify the LCP element in DevTools' Performance panel or PageSpeed Insights' Diagnostics tab. If it's text, this is a font-loading problem, not an image problem.
- Check its
loadingattribute in the rendered HTML (view source, not the theme editor). If it sayslazyand the element is above the fold, that's your fix — override it per section index as shown above. - Check for an opacity or visibility transition on that element, and trace what script removes it. If that script is bundled into a large deferred file, separate it or remove the transition.
- Confirm the element is a real
<img>, not a CSS background-image, wherever the design allows it. - Add
fetchpriority="high"to that one element only, and nothing else on the page.
This is deliberately narrower than a full Core Web Vitals audit. For CLS and INP, and for the governance side of keeping regressions from coming back after every app install, our speed optimization checklist covers the broader system; if the store in question is genuinely app-heavy and the slowdown traces to third-party scripts rather than the theme's own defaults, our triage guide for app-heavy themes is the more relevant read.
Who doesn't need to do this
If PageSpeed Insights already shows a good lab score and Search Console's Core Web Vitals report shows the same pages passing in the field, don't go looking for a problem that isn't there — chasing a marginal lab-score improvement on a page that already passes in real-world data is wasted engineering time. Likewise, if your LCP element is a heading rendered in a system font rather than an image, none of the fixes above apply; the fix is font-display strategy, which is a different problem with a different diagnosis.
If you've checked all five steps above and LCP is still failing in field data specifically — not just in a single Lighthouse run — the cause is more often a slow server response or a render-blocking app script than anything in the theme's image handling, and that's worth a proper audit rather than another round of image compression. That's the kind of diagnostic work we do directly; if you'd rather have someone trace it than keep guessing, talk to us about a performance pass.