Skip to main content
SEO

Shopify LCP: Fix the Hero Image Before Anything Else

Find your real LCP element in PageSpeed Insights or DevTools, then fix lazy-loaded heroes, fetchpriority, image_tag and animations on a Shopify theme copy.

November 4, 2026 19 min read Talha Butt.
Share

Shopify LCP: Fix the Hero Image Before Anything Else

Quick answer: Before you change anything, find out which element is your Largest Contentful Paint (LCP) element on mobile, because it is not always the hero image. If it is an image, the usual Shopify causes are that it is lazy-loaded, it has no fetchpriority="high", it is hidden behind an animation or script, or it is a CSS background the browser finds late. Google’s target is an LCP of 2.5 seconds or less at the 75th percentile of page loads (web.dev), and Shopify’s theme guidance says to load first-screen images eagerly and mark the LCP image with fetchpriority="high". Make the edits on a duplicate theme, then watch Shopify’s Web Performance report over the following weeks, not one PageSpeed run.

If your store feels slow on a phone, the usual advice is to compress everything, install a speed app or ask a developer to “optimise the theme”. That is a long way round. Often the slow part is one element on one template, and the fix can be a few attributes on a single image.

This guide is for the founder who has seen “Largest Contentful Paint” flagged in PageSpeed Insights or Search Console and wants to know what to do, in what order, and how to tell it worked.

A timing note. Black Friday is Friday 27 November 2026, with Cyber Monday on 30 November. Our Black Friday checklist advises freezing theme and app changes around 20 November, so if you are reading this after that date, take a baseline, build and test any fix on a duplicate theme, and be willing to wait until after Cyber Monday to publish it. Try all code in this post on a duplicate of your theme first, never on the live one.

What LCP measures, and what counts as good

web.dev defines LCP as the render time of the largest image, text block or video visible in the viewport, measured from when the visitor starts loading the page. Because the clock starts at navigation, it includes redirects, connection set-up and the wait for the first byte, not only the image download.

RatingLCP at the 75th percentileSource
Good2.5 seconds or lessweb.dev LCP guide
Needs improvement2.5 to 4 secondsweb.dev; PageSpeed Insights documentation
PoorMore than 4 secondsweb.dev; PageSpeed Insights documentation

Thresholds as at 31 October 2026. “75th percentile” means the target is met when three quarters of real page loads are at or below 2.5 seconds, which a quick test on good Wi-Fi does not show. LCP also stops being reported once a visitor taps, scrolls or presses a key.

The element can be an <img>, an image inside SVG, a video (its poster image or first frame, whichever is earlier), an element with a CSS url() background image, or a block of text. An image that has not finished loading does not count, and neither does text still waiting on a web font. web.dev also says Chromium browsers leave out invisible elements (opacity of 0), elements that cover the full viewport, which are treated as background, and low-detail placeholder images. So if your hero fills the whole phone screen, the LCP element Chrome reports may be something on top of it, such as the headline.

How common are images? web.dev cites the 2024 Web Almanac (HTTP Archive) finding that 73% of mobile pages across the web have an image as their LCP element. That is web-wide, not Shopify-specific. Shopify’s theme documentation says LCP is “usually a hero or product image”, that mobile devices account for the majority of Shopify storefront traffic, and that Core Web Vitals scores are measured on mobile by default. So fix the mobile page first.

Step 1: find the real LCP element

The most common mistake is to assume. An owner sees “LCP” and starts compressing the banner, when the LCP element on mobile is a headline or the first product photo. Find it first.

PageSpeed Insights

Go to pagespeed.web.dev, run the page and open the mobile tab. web.dev’s guide describes a top section of real-user data (“Discover what your real users are experiencing”) and a lower section of lab data (“Diagnose performance issues”). Labels change from time to time, so treat those names as a guide.

  • Real-user section. This is field data from the Chrome User Experience Report (CrUX). Google’s PageSpeed documentation says it covers the previous 28-day collection period at the 75th percentile. A page without enough visits falls back to site-wide (“origin”) data, which can look quite different. web.dev says to concentrate on real-user data first when it exists.
  • Lab section. In the diagnostics, find Largest Contentful Paint element. web.dev says it shows a breakdown of the timings that made up the LCP. Chrome’s LCP request discovery insight checks three things for an image: it is discoverable in the HTML, it has fetchpriority="high", and it is not lazy-loaded.

Run the home page, your top product page and your top collection page. Shopify’s performance score combines those three.

Chrome DevTools

On desktop Chrome, open DevTools and go to the Performance panel. Chrome’s documentation says it shows your local LCP as soon as it opens, and the LCP element can be hovered to highlight it on the page. Shopify’s fetchpriority guidance points to Performance > Insights to identify the element and its resource load delay, and to the Network panel with the Priority column enabled, to check the priority the image was given. Shopify’s testing guidance says to use an Incognito window so extensions do not interfere. The panel’s environment settings suggest device, CPU and network throttling to approximate a phone.

View the page source

Right-click, choose View page source, and search for the hero’s file name. An <img> with a src or srcset in the page’s own HTML means the browser can find the image early. If it appears only in a data-src attribute, a script or a stylesheet, it cannot. web.dev’s analysis of Chrome data (page updated October 2024) found that 35% of those image LCP elements had source URLs that were not discoverable in the initial HTML response. That is web-wide, not Shopify-specific.

Why lab and field disagree

web.dev explains that a lab test uses one device, network and location, and loads with a cold cache. Different screen sizes show different elements, so a lab device may have a text block as its LCP element while most real visitors see something else. Its conclusion: use field data to prioritise, and lab data to find further optimisations. If mobile and desktop disagree, check the LCP element on each.

Our guide to reading Shopify session recordings shows how to watch what the first screen does on a phone as it loads.

Step 2: read the four parts of LCP time

Once you know the element, web.dev splits LCP time into four parts that add up to the whole, with no overlap or gap.

PartGuideline share (web.dev)Typical Shopify cause when long
Time to first byte (TTFB)About 40%Slow server response, redirects, heavy Liquid
Resource load delayUnder 10%Image lazy-loaded, found late, or low priority
Resource load durationAbout 40%File too big for the screen, poor srcset and sizes
Element render delayUnder 10%Fade-in animation, scripts hiding the element, blocking CSS

web.dev calls these guidelines, not strict rules, and warns against turning them into time targets. If your LCP is consistently under 2.5 seconds, the proportions do not matter. When it is not, they point at the cause. Two parts are named “delay”, and web.dev’s advice is to get those as close to zero as possible.

Here is an illustration with made-up numbers. A mobile product page has an LCP of 4.2 seconds: TTFB 0.6 s (14%), load delay 1.5 s (36%), load duration 0.9 s (21%), render delay 1.2 s (29%). The two delay parts are 2.7 of the 4.2 seconds, and neither is about file size. Compressing the image would attack the 0.9 seconds and leave the 2.7. This is arithmetic for an example, not a measurement of any store, and a real fix will not necessarily recover all of it.

Chrome real-user data, analysed in web.dev’s article on common Core Web Vitals problems, points the same way. The majority of origins with poor LCP spend less than 10% of their 75th-percentile LCP time downloading the LCP image, and among pages with poor LCP the image’s loading was delayed by 1,290 milliseconds at the 75th percentile. That is web-wide data, but it suggests the wait is often before the download, not during it.

Step 3: fix it by cause

Work through these in order, cheapest first. Re-measure the breakdown after each change.

1. The hero is lazy-loaded

Browser lazy loading (loading="lazy") defers an image until the browser works out it is near the viewport. That suits images lower down and is wrong for the first screen. web.dev says never to lazy-load your LCP image, because it always adds unnecessary load delay. Shopify’s documentation calls it one of the most common and damaging performance anti-patterns in Shopify themes. How it happens:

  • The default is overridden. Shopify’s image_tag filter sets loading automatically: eager for sections 1 to 3, lazy from section 4, and eager whenever section.index0 is nil (the theme editor, static sections, the Section Rendering API). If a developer or app hard-coded loading="lazy" on every image, the default is gone.
  • A hand-written condition goes the wrong way. Shopify warns that {% if section.index <= 3 %} takes its else branch when the index is nil, because a comparison with nil is false in Liquid. If that branch lazy-loads, the hero is lazy in the theme editor but not necessarily on the live page. Check the preview link and the live page, not only the editor.
  • A JavaScript lazy-loading library. Libraries such as lazysizes swap src for data-src, hiding the address from the browser until a script runs. Shopify’s fix is to put src back, add loading="lazy" only below the first screen, and remove the library.
  • Counting sections wrongly. section.index counts per location, not per page, and disabled sections do not use an index. “First” means first in its own location.

Shopify’s documented pattern, adapted for a hero:

{%- if section.index > 3 -%}
  {{ section.settings.image | image_url: width: 2000
     | image_tag: loading: 'lazy', widths: '600, 900, 1200, 1600, 2000', sizes: '100vw' }}
{%- elsif section.index == 1 -%}
  {{ section.settings.image | image_url: width: 2000
     | image_tag: loading: 'eager', fetchpriority: 'high', widths: '600, 900, 1200, 1600, 2000', sizes: '100vw' }}
{%- else -%}
  {{ section.settings.image | image_url: width: 2000
     | image_tag: loading: 'eager', widths: '600, 900, 1200, 1600, 2000', sizes: '100vw' }}
{%- endif -%}

The lazy case is a positive test (section.index > 3), so everything else, including nil, falls through to eager. If your theme already uses image_tag with no loading value, you may need no change. Check what the page outputs rather than editing blind.

2. The LCP image has no fetchpriority="high"

Shopify says browsers find images at low priority and raise it only after layout, which leaves a gap before a critical download starts; fetchpriority="high" closes it. web.dev calls it a hint, not a directive. The rules, from Shopify’s page and web.dev:

  • Use it on the LCP image only. web.dev says high priority on more than one or two images makes it unhelpful.
  • Be certain it is the LCP element and that it is in the first screen.
  • Pair it with loading="eager" or no loading attribute, never with loading="lazy".
  • web.dev adds the reverse: fetchpriority="low" suits images early in the HTML that are not visible at startup, such as hidden carousel slides.

With image_tag you pass it like any HTML attribute, as in the snippet above. Dawn, Shopify’s reference theme, does this: in its current source on GitHub (checked 31 October 2026) the image banner sets fetchpriority to high when section.index == 1. Many stores run paid or older themes, so check your own code.

On preloading: image_tag has a preload option, but it sends a Link header and does not change the <img> tag, and Shopify says to use it sparingly, for one or two resources the browser finds late. Its platform documentation adds that every hint takes a slot in a limited header, so overuse pushes out the hints that matter. For a normal server-rendered <img> with fetchpriority="high", a preload on top is usually unnecessary.

3. The hero is a CSS background image

If a page builder, app or theme sets the banner as background-image, the browser cannot find it by scanning the HTML. Shopify lays out the chain: HTML downloads, then the CSS file is found and downloads, then it is parsed, then the image address is discovered, then the download starts. Shopify recommends an <img> positioned behind the text (its example uses position: absolute; inset: 0; object-fit: cover), which also allows fetchpriority, srcset and sizes. Backgrounds remain fine for decorative patterns and anything below the first screen. If you cannot change the markup, web.dev’s fallback is to preload the image, either with a link rel="preload" tag in the HTML or through a Link header.

4. Separate mobile and desktop hero images

Many themes output two images and hide one with CSS. Shopify notes the browser cannot know one is hidden, so it downloads both, and fetchpriority="high" often lands only on the desktop image, leaving the mobile LCP image at default priority. Shopify’s fix is a single <picture> element with <source media="..."> entries and fetchpriority="high" on the inner <img>. It is a bigger edit than the others, so ask whoever built your theme whether it already uses <picture>.

5. Sliders and carousels

The first slide loads eagerly with high priority; the others do not. web.dev’s fetchpriority article warns that hidden slides can be treated as close enough to the viewport to load at higher priority, so carousels need deliberate settings. Dawn’s slideshow is the model: high priority on the first slide when section.index == 1, and loading: 'lazy' on later slides. If your slider is an app, check what it outputs for slide one.

6. The image is hidden behind an animation, a script or an app

This is element render delay: the image has arrived but cannot show. web.dev lists the causes: stylesheets or synchronous scripts in the <head> still loading, the element not yet added to the page because it waits for JavaScript, the element hidden by other code such as an A/B testing library, and a busy main thread. On Shopify, check:

  • Fade-in and page transitions. Because Chrome ignores opacity: 0 elements, an image that starts invisible counts only once it is visible. Shopify calls page transitions the most impactful animation-related LCP issue and the easiest to fix. Look in Online Store > Themes > Customize > Theme settings > Animations. Dawn has a “Reveal sections on scroll” setting, on by default in its source. Whether it affects your LCP depends on your theme, so switch it off on your duplicate and re-measure.
  • A/B testing snippets. Shopify says anti-flicker snippets hide content until the testing script loads, and to remove them when no test is running. Ask whoever set up testing to list every tool still switched on.
  • Pop-ups. Shopify recommends avoiding full-screen overlays on page load.
  • Apps. Shopify advises removing or deferring app scripts that block HTML parsing before content renders. If an app install lines up with a jump in LCP, that is your lead. Before adding another, see our guide to what Shopify SEO apps do.
  • Fake performance apps. Shopify tells merchants to avoid apps that cheat Lighthouse or PageSpeed tests. A good lab score from one does not help real visitors.

7. The image is the wrong size

Only after the delay parts do I look at file size. Use image_url and image_tag rather than hand-built CDN addresses, and srcset and sizes so each screen gets the smallest image that still looks good. Check the sizes on your hero: a phone should not download the 2,000-pixel version. Shopify also says to give the LCP image an explicit sizes value rather than letting a script calculate it, since the browser cannot choose a source until the script runs.

8. The page itself is slow to start

TTFB is about 40% of the guideline. web.dev says a high TTFB can make a 2.5-second LCP challenging or even impossible, and lists it last because developers often have the least control over it. Shopify’s documentation says that if the server response is slow, look at your Liquid; if it is fast, look at the browser-side work that follows. web.dev names redirects as one cause, and Shopify lists limiting how many items a Liquid array fetches as a TTFB fix, so check for redirect chains on landing pages and heavy loops over large collections in theme code. You may need a developer here.

Make the edit safely

  1. Duplicate the theme. Shopify’s help says to go to Online Store, open the actions menu on the theme and click Duplicate. The copy appears as “Copy Of” plus the theme’s name. With 20 themes already, you must delete one first, and deleting is permanent.
  2. Edit the copy, one change at a time. Remove lazy loading, test, then add fetchpriority, test again. That is how you learn what worked.
  3. Measure at least three times. Shopify’s testing guidance says a single Lighthouse run can vary by 5 to 10 points, to use the median of several runs and to keep the same device and network conditions.
  4. Check other templates and a real order. A shared snippet can affect collection cards and product pages. Walk through add to cart and checkout on a real phone.
  5. Publish deliberately and keep the original until the field data has moved. Do not change checkout code based on this guide.

If you do not edit code, you can still switch off a page transition, remove an app with a blocking script, or upload a smaller hero. Or give a developer a precise brief: “On mobile the LCP element is the first product image. It is lazy-loaded and has no fetchpriority. Please fix it on a duplicate theme and send the preview link.”

Check it worked: lab first, then field

A better lab result is a good sign, not proof. Re-run PageSpeed Insights on the duplicate’s preview and compare the LCP breakdown: load delay and render delay should shrink if those were your causes. After publishing, three field sources matter:

  • PageSpeed Insights field data covers the previous 28 days, so it moves slowly.
  • Search Console’s Core Web Vitals report uses real-world CrUX data, groups similar URLs, and reports mobile and desktop separately. A group’s status is its worst metric. When you start validating a fix, Google monitors for 28 days. That is a monitoring window, not a promised result.
  • Shopify’s Web Performance reports, next.

What to check in the Shopify Web Performance report

It is based on real visitors to your store, so it is the most direct check on a hero fix. Per Shopify’s help documentation, as at 31 October 2026:

Where. The summary is on the Themes page (Online Store). The full reports are under Analytics > Reports, where searching a colon (:) lists them all and the keyword “contentful” finds the LCP ones. You need the Reports staff permission.

What exists. Three LCP reports: Over Time, Page URL and Page Type. They default to the 75th percentile and can be filtered by device type. Shopify’s ratings (Good up to 2,500 ms, Moderate to 4,000 ms, Poor above) match web.dev.

What I do with them:

  1. Filter to mobile and compare with desktop. Good on desktop and Moderate or Poor on mobile points at the phone layout.
  2. Use Page Type to see whether it is the home, product or collection template. A home-page hero fix will not move a product-page LCP.
  3. Use Page URL to find the high-traffic pages in the poor range. Those go into PageSpeed Insights.
  4. Use Over Time and its annotations. Shopify says its graphs carry event annotations for changes such as app installs and theme updates, so you can match a rise in LCP to a date.

Limits. Data can be delayed by up to 36 hours and covers only the last 90 days. Metrics may not change straight away after an edit, and a new store or one in private mode might have none. On low traffic, Shopify suggests a weekly or monthly filter because scores swing. Its reports use UTC and all Chromium browsers plus Firefox, while PageSpeed Insights uses CrUX from opted-in Chrome users only, so two honest tools can disagree.

One inconsistency. As at 31 October 2026, Shopify’s reports page says the summary uses real user data from the past 30 days, while its overview page says the performance score uses the last 7 days. Check the date picker in your own admin and do not compare numbers from different windows.

Google says it highly recommends good Core Web Vitals for success with Search and that they align with what its core ranking systems seek to reward. Shopify’s help says better web performance can improve the shopping experience and conversion. Those are reasonable grounds for fixing a clear problem. They are not a promise that your rankings or sales will move, and I will not quote a “every second costs X%” figure, because I could not verify one at a primary source.

Measure your own instead. Record mobile conversion rate and LCP before the change, read the result over a long enough window, and remember other things changed too. Our guide to a good Shopify conversion rate explains the baseline, and the mobile conversion guide covers what else to check on a phone.

LCP also does not replace the rest of SEO: a fast page that does not answer the search still does not rank. Our Shopify SEO guide shows where it fits, the product page SEO checklist covers the content of the page you are fixing, and the Shopify SEO and CRO resource hub links the rest.

A 30-minute LCP check

  1. Run PageSpeed Insights (mobile) on the home page, top product page and top collection page. Note the field LCP and the lab LCP element for each.
  2. In Shopify’s report, search “contentful”, filter to mobile, then read Page Type and Page URL.
  3. For the worst page, view the source and check the LCP image for loading="lazy", fetchpriority and a real src or srcset. Read the breakdown in DevTools > Performance.
  4. Duplicate the theme, fix the one cause you found, measure three times, then publish and review the report after a few weeks.

Key takeaways

  • Find the real LCP element on mobile first. It may not be the hero image.
  • Good is 2.5 seconds or less at the 75th percentile of real visits; over 4 seconds is poor.
  • Split the time into four parts. Check load delay and render delay before compressing files.
  • Never lazy-load the LCP image. Put fetchpriority="high" on it, and on one or two images at most.
  • Look for CSS background heroes, separate mobile and desktop images, sliders, fade-ins, A/B test snippets and app scripts.
  • Edit a duplicate theme, one change at a time, and measure more than once.
  • Judge results with field data: PageSpeed Insights, Search Console and Shopify’s report, allowing for their different windows and data sources.
  • Do not expect a promised ranking or sales result. Measure your own before and after. Shopify’s own overview says there is no such thing as a perfect speed score.

FAQs

What is a good LCP score for a Shopify store?

2.5 seconds or less at the 75th percentile of page loads, according to web.dev. Between 2.5 and 4 seconds needs improvement, and over 4 seconds is poor. Shopify’s Web Performance report uses the same boundaries and labels them Good, Moderate and Poor.

Is the hero image always my LCP element?

No. It is whichever eligible element (image, video poster, background image or text block) is largest in the first screen when the page renders. On mobile the hero may be cropped, or full-viewport elements may be ignored, so a heading or another image can be the LCP element. Check the “Largest Contentful Paint element” diagnostic in PageSpeed Insights or the DevTools Performance panel on each template.

Should I lazy-load my hero image?

No. web.dev says never to lazy-load the LCP image, and Shopify says images visible in the initial viewport should load eagerly. Lazy loading is for images below the first screen. Shopify’s image_tag filter applies eager loading to the first three sections by default, but a theme or app can override it.

How many images should have fetchpriority=“high”?

One, or two at most. web.dev says high priority on more than one or two images makes it unhelpful for LCP, and Shopify says to use it on the LCP image only. It is a hint, so the browser can still make its own decisions.

Why does PageSpeed Insights show a different LCP from my Shopify report?

They use different data. PageSpeed Insights’ top section is CrUX field data over 28 days from opted-in Chrome users, and its lower section is a simulated lab test. Shopify’s report uses real visitors on all Chromium browsers and Firefox, in UTC, over the window you pick.

How long until a fix shows in my reports?

A lab re-test shows a change straight away. Field data takes weeks: PageSpeed Insights covers 28 days, Search Console monitors a fix for 28 days after you start validation, and Shopify says its data can lag by up to 36 hours.

Will fixing LCP improve my Google rankings or my sales?

Nobody can promise that. Google says good Core Web Vitals align with what its ranking systems seek to reward, and Shopify says better performance can improve conversion, but relevance and content still matter. Measure your own mobile conversion rate and LCP before and after, over a long enough window.

Want your store’s Core Web Vitals read by someone who will tell you what is worth fixing?

Core Web Vitals sit inside the technical audit of our Shopify SEO service, alongside crawling and indexation. The hard part is not running PageSpeed Insights. It is knowing which template, which element and which app is behind the number, and whether fixing it is worth your time. If you would like a second pair of eyes, book a free 30-minute diagnostic or tell us about your store. We look at your store before we recommend anything, and we tell you honestly if SEO isn’t the right move. No pitch.

Free 30-min diagnostic

Seen enough?
Book a free diagnostic.

We'll look at your store's search pages, find what is costing you traffic and tell you what to fix first. No pitch. No email gate. 30 minutes.

More from Prism3