Fix Hero Banner Eager Loading in Shopify (2026)

Last updated
Expert reviewed
5 min read
Jacques Blom
Jacques Blom
CTO at Fudge.

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"> tag in theme.liquid <head>. 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 itself - except for the preload hint.
  • Not a developer? The easiest and safest way to make this fix is with Fudge. Describe the change in plain English and it edits the section files for you - no Liquid, no theme duplication, no risk of breaking anything.

If a Lighthouse or PageSpeed Insights audit just told you your hero image is lazy-loaded - or that the LCP element was preloaded too late - you’re in the right place. This guide walks through the exact Liquid edits to make your hero load eagerly, with fetchpriority and a preload hint where needed.

Why you can trust us

We’ve worked with hundreds of Shopify brands on storefront performance, and we built Fudge - an AI storefront editor with a 5.0 rating on the Shopify App Store. The patterns below are the ones we apply ourselves and recommend in audits.


Why your hero image is 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.


How to diagnose it

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

Run pagespeed.web.dev on your store. Under Diagnostics, the “Largest Contentful Paint element” entry shows which element is being measured. If it’s your hero and the “Load Delay” metric is high, the image is being fetched too late.

Related: 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:

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 three, 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 in seconds? Describe it to Fudge.
Try Fudge for Free

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 in theme.liquid <head>. This tells the browser to start fetching the image immediately, before CSS even parses.

Step 1. Open layout/theme.liquid.

Step 2. Inside the <head> block, add a conditional preload that only fires on the page where the hero lives (usually the homepage):

{%- if template.name == 'index' -%}
  {%- assign hero_section = sections['image-banner'] -%}
  {%- if hero_section.settings.image -%}
    <link
      rel="preload"
      as="image"
      href="{{ hero_section.settings.image | image_url: width: 2000 }}"
      fetchpriority="high"
    >
  {%- endif -%}
{%- endif -%}

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="{{ hero_section.settings.image | image_url: width: 800 }} 800w,
               {{ hero_section.settings.image | image_url: width: 1200 }} 1200w,
               {{ hero_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 “Largest Contentful Paint element” diagnostic should show a lower Load Delay. LCP overall should drop, often by 500-2000ms on a slow mobile connection.

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.


Easiest fix for non-developers: use Fudge

If you’re not a developer, Fudge is the recommended way to 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. No Liquid syntax. No risk of breaking other sections.

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 in theme.liquid for the index template.”

Fudge handles both the image_tag edit in your section file and the <link rel="preload"> in theme.liquid - the same two-step fix described above, applied safely without you touching code.


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 scope your preload hints with if template.name == 'index' etc.

Your image isn’t being served as WebP. Shopify’s CDN serves WebP 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.

Related: Lazy Load Images in Shopify.

Related: Compress Images in Shopify.

Related: Fix Render-Blocking Scripts in 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 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"?

No. Use loading="eager" for the LCP element only — typically one hero image. Other above-the-fold images (logo, nav icons, secondary banners) are small enough that lazy-loading vs eager loading doesn’t meaningfully affect LCP. Only fetchpriority="high" on one image per page is also the rule.

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?

Theme.liquid is the conventional location since the <link rel="preload"> must be in <head> and theme.liquid wraps every page. Some sections support a {% layout 'theme' %} injection point, but most don’t. If you can’t edit theme.liquid directly, Fudge can make the edit safely without touching code.

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 “Load Delay” should drop and overall LCP score 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. Web.dev’s recommendation on avoiding CSS background-image for LCP elements: see Optimizing images for performance on Shopify.

You might also be interested in

How to Lazy Load Videos in Shopify (2026)
Add lazy loading to videos in Shopify using the facade pattern for YouTube and Vimeo embeds. Reduces page weight by deferring iframe loads.
How to Minify CSS and JavaScript in Shopify (2026)
How Shopify handles CSS and JS minification automatically, when you still need to minify manually, and the best tools for custom code blocks.
Shopify Lazy Load Images: How to Set It Up (2026 Guide)
Lazy load images in Shopify the right way. How to check if your theme already does it, add it manually with `loading="lazy"`, and why your hero image should always load eagerly.