Fix Hero Banner Eager Loading in Shopify (2026)

Last updated
•
Expert reviewed
•
5 min read

Key takeaways

  • Shopify’s image_tag filter auto-applies loading="lazy" to images past the first three sections - your hero needs loading="eager" set explicitly.
  • Add fetchpriority="high" to the hero <img> to push it to the front of the browser’s download queue.
  • For CSS background-image heroes, add a <link rel="preload"> for the image at the top of the hero section file. The URL must match exactly or the browser downloads the image twice.
  • The fix lives in sections/image-banner.liquid (or slideshow.liquid/your hero section), not theme.liquid.
  • Not a developer? Fudge can make this fix for you: describe the change and it edits the section files as a draft you preview and approve.

To fix a lazy-loaded hero banner in Shopify, set loading="eager" and fetchpriority="high" on the hero image in its section file, and add a preload hint if the hero is a CSS background. If an audit flagged your hero with “fetchpriority=high should be applied” or “This img tag is above the fold and could be eagerly-loaded to improve performance.”, these are the exact Liquid edits that clear it.

Why you can trust us

We’ve worked with hundreds of Shopify brands on storefront performance, and we build Fudge, the AI operator for Shopify stores, with a 4.9 rating on the Shopify App Store. The patterns below are the ones we apply ourselves and recommend in audits.


Why is my Shopify hero image lazy-loaded?

A few things commonly cause it:

1. Shopify’s default behaviour. When you render an image with the image_tag filter and don’t set the loading attribute, Shopify applies loading="lazy" to any image past the third section in the template1. If your hero section is the fourth on the page (because of an announcement bar, a sticky promo, or an app section above it), it gets lazy-loaded by default.

2. The theme hard-codes loading="lazy". Some older themes apply loading="lazy" indiscriminately to every image, including the hero.

3. The hero is a CSS background-image. When the hero is rendered as background-image: url(...) instead of an <img> tag, the browser doesn’t see it during the initial parse. There’s no loading attribute to set, and fetchpriority doesn’t apply.

4. An app or customisation overrode it. Some apps inject scripts that swap eager for lazy on every image to “improve performance”. This is the opposite of what you want for the LCP element.

For lazy loading the images further down the page, see how to lazy load images in Shopify.


How do I check if my hero image is lazy-loaded?

Right-click the hero image on your storefront → Inspect. Look at the <img> tag in the HTML.

  • loading="lazy" is the problem. Replace it with loading="eager".
  • Without fetchpriority, the browser treats it as a normal-priority image and may fetch it after scripts and fonts. Add fetchpriority="high".
  • No <img> tag at all means it’s a CSS background-image - skip to the preload section below.

Run pagespeed.web.dev on your store. Under Insights, open LCP request discovery and LCP breakdown. If the hero shows “lazy load not applied” or “fetchpriority=high should be applied”, or its resource load delay is high, the image is fetched too late.

For the wider set of speed fixes, see how to speed up a Shopify theme.


Fix the <img> tag in your hero section

The hero usually lives in one of these section files - not theme.liquid:

  • sections/image-banner.liquid (Dawn-based themes)
  • sections/slideshow.liquid
  • sections/hero.liquid or sections/featured-hero.liquid (custom themes)

Step 1. Duplicate your theme. Always.

Step 2. Open the relevant section file in the code editor.

Step 3. Find the <img> tag or the image_tag filter call. Older themes look like this:

<img
  src="{{ section.settings.image | image_url: width: 2000 }}"
  alt="{{ section.settings.image.alt }}"
  width="2000"
  height="1000"
  loading="lazy"
/>

Step 4. Change loading="lazy" to loading="eager" and add fetchpriority="high":

<img
  src="{{ section.settings.image | image_url: width: 2000 }}"
  alt="{{ section.settings.image.alt }}"
  width="2000"
  height="1000"
  loading="eager"
  fetchpriority="high"
/>

If your theme uses the modern image_tag filter:

{{ section.settings.image
  | image_url: width: 2000
  | image_tag: loading: 'eager', fetchpriority: 'high', alt: section.settings.image.alt
}}

Step 5. Save and reload. Right-click the hero, Inspect, and confirm the new attributes are in place.


The cleaner pattern: section.index

Hard-coding loading="eager" works, but the section can be moved (a merchant drags it down the homepage in the Theme Editor) and suddenly it’s no longer the LCP element while still loading eagerly. The portable pattern uses section.index:

{%- liquid
  assign loading = 'eager'
  assign fetchpriority = 'auto'
  if section.index == 1
    assign fetchpriority = 'high'
  elsif section.index > 2
    assign loading = 'lazy'
  endif
-%}

{{ section.settings.image
  | image_url: width: 2000
  | image_tag: loading: loading, fetchpriority: fetchpriority, alt: section.settings.image.alt
}}

This says: if I’m the first section on the page, fetch me with high priority. If I’m in the first two, load me eagerly. Otherwise, lazy load. It’s the pattern Shopify itself recommends for any image section that might end up in or out of the LCP slot1.

Want this fix made for you? Describe it to Fudge.
Start free trial

Preload a CSS background-image hero

If your hero is rendered as background-image: url(...) in CSS, the browser doesn’t know about the image until it parses your stylesheet, downloads it, and applies it to the element. By that point the LCP clock has been ticking.

The fix is a preload hint. It tells the browser to start fetching the image while it parses the HTML, before the stylesheet is downloaded and applied.

Put the hint in the hero section file, not theme.liquid. Layout files cannot read a section’s settings, so theme.liquid has no way to look up the image a merchant picked for the hero. A <link rel="preload"> is allowed in the body, and the browser still finds it early in the HTML.

Step 1. Open the hero section file, for example sections/image-banner.liquid.

Step 2. At the top of the section’s markup, add a preload that only renders when the section is first on the page:

{%- if section.index == 1 and section.settings.image -%}
  <link
    rel="preload"
    as="image"
    href="{{ section.settings.image | image_url: width: 2000 }}"
    fetchpriority="high"
  >
{%- endif -%}

If you want the hint in <head> instead, hard-code the image URL in layout/theme.liquid (copy it from the rendered page) and update it whenever the hero image changes.

The URL in your preload tag must match the URL the browser would otherwise request - exactly. Same width parameter, same crop, same format. If they differ by a single character, the browser fetches both and you’ve made things worse.

Step 3. If your image varies by viewport (mobile gets a different crop), use imagesrcset and imagesizes so the browser preloads the right one:

<link
  rel="preload"
  as="image"
  imagesrcset="{{ section.settings.image | image_url: width: 800 }} 800w,
               {{ section.settings.image | image_url: width: 1200 }} 1200w,
               {{ section.settings.image | image_url: width: 2000 }} 2000w"
  imagesizes="100vw"
  fetchpriority="high"
>

Step 4. Better still: convert the CSS background-image to a real <img> tag inside the section. You get fetchpriority, automatic srcset, and Shopify’s own HTTP-header preload behaviour. CSS backgrounds for the LCP element are an anti-pattern2.


Don’t fetchpriority="high" on every image

fetchpriority="high" is a finite resource. If you mark five images as high priority, the browser has to choose - and it’ll deprioritise the others to compensate. Set it on one image per page: the LCP element.

The same applies to preload hints. One per page. Multiple preloads competing for bandwidth slow down the very thing you’re trying to speed up.


Verify the fix

1. Inspect the rendered HTML. Confirm loading="eager" and fetchpriority="high" are on the hero <img>.

2. Open Chrome DevTools → Network tab → reload with cache disabled. Sort by Priority. Your hero image should be at or near the top with priority “Highest”.

3. Re-run pagespeed.web.dev. The LCP breakdown insight should show a lower resource load delay, and LCP overall should drop.

4. If you added a preload, open the Network tab and search for the image URL. It should appear once, not twice. If you see two entries, the preload URL doesn’t match the rendered image URL - fix the discrepancy.


Fix it without code: use Fudge

If you’re not a developer, Fudge’s store editor can make this fix. You describe the change in plain English - Fudge edits the section files, shows you a preview against your live theme, and only writes to your store when you approve.

No theme duplicating and no Liquid syntax.

Use a prompt like:

“On the homepage hero section, set loading=eager and fetchpriority=high on the banner image. If it’s a CSS background, add a preload hint for the banner image.”

Fudge handles both the image_tag edit and the <link rel="preload"> hint - the same two-step fix described above - as a draft you preview before publishing, without you touching code. If you’re not sure the hero is your only problem, ask Fudge for a speed audit first: it lists what slows your pages down and drafts each fix for you to approve.


Common pitfalls

The hero is in a slideshow with multiple slides. Only the first slide is visible on initial paint. Apply eager loading and fetchpriority="high" to the first slide’s image only - lazy load the rest.

The hero changes by template. A homepage hero and a collection page hero are different sections. Apply the fix to each, and keep the preload hint in whichever section sits first on that template.

Your image isn’t being served as WebP. Shopify’s CDN serves WebP or AVIF automatically when the browser supports it - but only if you go through image_url or img_url. A raw src="...assets/banner.jpg" won’t get WebP. Use the Liquid filters.

An app is overriding your changes. If you fix the section but Inspect still shows loading="lazy", an app’s JavaScript is mutating the DOM after page load. Check your installed apps for “performance” or “image optimisation” tools - some of them re-apply loading="lazy" to everything indiscriminately.

A hero that loads late without reserved space also shifts everything below it. For that problem, see Cumulative Layout Shift on Shopify.


When to expect a meaningful LCP drop

If your hero was lazy-loaded and large, expect a 1-3 second LCP improvement on slow 4G. If it was already eager but missing fetchpriority, expect 200-800ms. If you also fix a CSS background-image with a preload hint, the gain stacks on top.

If you make these changes and PageSpeed still flags the LCP as slow, the bottleneck has moved elsewhere - render-blocking CSS or JavaScript, server response time, or a hero image that’s simply too heavy. Compress the image first, then look at render-blocking scripts.


FAQ

Why does Shopify lazy-load my hero image by default?

Shopify's image_tag filter applies loading="lazy" to images past the third section in the template. If your hero is the 4th section (e.g., after announcement bar, sticky promo, app section), it gets lazy-loaded. This is good default behavior for non-LCP images but harmful for the hero — explicitly set loading="eager".

Should every above-the-fold image use `loading="eager"`?

Yes. Above-the-fold images should stay eager, which is the default when no loading attribute is set; only lazy-load images below the fold. Reserve fetchpriority="high" for the one LCP image, usually the hero.

Will `fetchpriority="high"` on every image speed up my Shopify page?

No — the opposite. fetchpriority="high" is a relative signal. If multiple images are marked high, the browser deprioritises the actual LCP element to compensate. Set it on exactly one image (the LCP element) per page. Same for preload hints — one per page.

Can I add a preload hint without editing theme.liquid?

Yes. Layout files cannot read a section's settings, so the simplest place for a hero preload is the top of the hero section file, where the browser still finds it early in the HTML. To keep the hint in <head>, hard-code the image URL in theme.liquid instead. If you would rather not edit either file, Fudge can make the change as a draft for you to approve.

How do I verify the eager-loading fix worked?

Three ways: (1) Right-click hero → Inspect → confirm loading="eager" and fetchpriority="high" are on the <img> tag, (2) Chrome DevTools → Network tab → reload → sort by Priority → hero should be Highest, and (3) re-run PageSpeed Insights: the LCP breakdown should show a lower resource load delay and overall LCP should improve.

Jacques's signature
Fix performance issues by describing what you want.

Footnotes

  1. Shopify’s official guidance on the image_tag filter and section.index defaults: Announcing new Liquid features for better web performance. ↩ ↩2

  2. Shopify Performance blog, Optimizing images for performance on Shopify. ↩

You might also be interested in

Jacques Blom
Jacques Blom
CTO at Fudge.