100% Working Shopify Lazy Loading Optimization

100% Working Shopify Lazy Loading Optimization (Faster Shopify Pages)

Lazy loading defers the download of images, videos, and scripts until a visitor actually needs them. On a Shopify collection page with 24 products, lazy loading can cut the initial page weight by 60 to 70 percent, loading only what is visible and fetching the rest as the visitor scrolls. Get the implementation right - native browser lazy loading for images, Intersection Observer for scripts and sections - and your PageSpeed scores improve immediately without removing a single piece of content.

70%
Initial page weight reduction on collection pages
95%+
Browser support for native loading="lazy"
2MB
Deferred image weight on a 24-product collection page
2s
Typical Time to Interactive improvement from script deferral

What Is Lazy Loading and Why Shopify Stores Need It

When a browser loads a web page, its default behavior is to download everything referenced in the HTML: every image, every script, every stylesheet, every embedded widget. For a content-rich Shopify store, this means a visitor loading your collection page downloads 24 product images, 3 section background images, a review widget, a wishlist app, and a currency converter - all before they have scrolled past the first row of products.

Lazy loading changes this default. Instead of loading everything at once, the browser loads only what is visible in the current viewport. Resources below the fold are queued and downloaded as the visitor scrolls toward them.

Shopify lazy loading before and after collection page weight comparison showing without lazy loading all 24 product images downloading at once with a total weight of 2.4MB and a slow red indicator versus with lazy loading only the first 4 images loading eagerly at 400KB and the remaining images loading progressively as the visitor scrolls with a fast green indicator
Shopify Lazy Loading Before and After - enabling lazy loading on a 24-product collection page drops initial page weight from 2.4MB to 400KB, with the remaining 2MB loading progressively as the visitor scrolls
Shopify Generates Content-Rich Pages

Themes are built with multiple sections, featured collections, trust badges, and review areas that push significant content below the fold on every page type. Without lazy loading, all of it downloads before the visitor sees the second row of products.

App Scripts Compound the Problem

Each app loads resources on page load by default. Without lazy loading, a store with 10 apps loads all 10 apps' resources before the visitor sees the second row of products.

Mobile Connections Are the Primary Traffic Source

On a throttled 4G connection, downloading 2.4MB upfront instead of 400KB is the difference between a 3-second load and a 9-second load. Lazy loading is not optional for mobile-first stores.

What to Lazy Load in Shopify

Shopify lazy loading decision framework flowchart with two paths: top path labeled load immediately eagerly showing hero image first product image navigation header and above fold fonts, bottom path labeled lazy load showing collection grid images below fold section backgrounds video embeds review widgets Instagram feeds and chat widgets
Shopify Lazy Loading Decision Framework - the rule is simple: if the visitor can see it without scrolling, load it immediately; if they need to scroll to reach it, defer it

Not everything should be lazy loaded. Getting the scope right is as important as the implementation itself.

Lazy Load These
  • Product images in collection grids (except the first 4 to 8)
  • Gallery images on product pages (except the first)
  • Section background images below the fold
  • Video embeds (YouTube, Vimeo, MP4 background videos)
  • Review carousels, Instagram feeds, recommendation sections
  • Chat widgets, heatmap tools, social proof notifications
Never Lazy Load These
  • Your hero image or main product image (LCP elements)
  • The first row of product images on a collection page
  • Navigation and header assets
  • Scripts that above-the-fold interactions depend on
  • Fonts used in above-the-fold text (causes CLS)
The rule is straightforward: if the visitor can see it without scrolling, load it immediately. If they need to scroll to reach it, defer it. Applying this consistently is the entire lazy loading strategy.

Native Browser Lazy Loading vs JavaScript Lazy Loading

Native browser lazy loading versus JavaScript Intersection Observer comparison infographic showing left panel with the HTML loading lazy attribute labeled simple no JavaScript required works on images and iframes and right panel showing Intersection Observer with a JavaScript code snippet labeled flexible works on scripts sections and videos both panels with pros and cons listed
Native Browser Lazy Loading vs Intersection Observer - use the loading attribute for images and iframes, use Intersection Observer for scripts, videos, and section initializations; they work better together than either does alone

There are two ways to implement lazy loading in Shopify: the native browser loading="lazy" attribute and JavaScript-based implementations using Intersection Observer. Understanding when to use each determines the quality of your implementation.

Native Lazy Loading: loading="lazy"

Built into modern browsers. No JavaScript required. No library to load. No extra weight added to the page. Works at the browser rendering engine level, which is faster and more efficient than any JavaScript implementation. Browser support exceeds 95 percent globally.

The browser determines what counts as "near the viewport" itself. The threshold varies by browser and network speed but is typically 1 to 3 screen heights below the current scroll position. Content loads before the visitor reaches it, so there is rarely a visible delay.

JavaScript Lazy Loading: Intersection Observer

A browser API that fires a callback when an observed element enters or exits the viewport. Runs off the main thread, meaning it does not add to JavaScript execution time the way scroll event listeners do.

Use when you need to lazy load entire sections of content, defer script initialization until a section is visible, set custom thresholds, or handle videos and app widgets that the loading attribute does not cover.

Use both together. loading="lazy" for all images and iframes below the fold. Intersection Observer for app scripts, section initializations, and video embeds. They are not mutually exclusive and work better in combination than either does alone.

How to Lazy Load Images in Shopify

Implementing lazy loading for images in Shopify Liquid is direct. The key is combining the loading attribute with correct dimension attributes to prevent CLS.

For product images in collection grids:

{% assign eager_count = 4 %}

{% for product in collection.products %}
  {% assign lazy = forloop.index > eager_count %}
  <img
    src="{{ product.featured_image | image_url: width: 400, format: 'webp' }}"
    loading="{{ lazy | ternary: 'lazy', 'eager' }}"
    width="{{ product.featured_image.width }}"
    height="{{ product.featured_image.height }}"
    alt="{{ product.featured_image.alt | escape }}"
  >
{% endfor %}
Adjust eager_count based on your grid columns. A 3-column grid on desktop should use eager_count = 6 to cover two rows. A 4-column grid should use eager_count = 8. The goal is to cover everything visible above the fold on the first load.

For product gallery images:

{% for image in product.images %}
  <img
    src="{{ image | image_url: width: 800, format: 'webp' }}"
    loading="{{ forloop.first | ternary: 'eager', 'lazy' }}"
    fetchpriority="{{ forloop.first | ternary: 'high', 'auto' }}"
    width="{{ image.width }}"
    height="{{ image.height }}"
    alt="{{ image.alt | escape }}"
  >
{% endfor %}

The first image gets fetchpriority="high" to ensure it loads as the LCP element. All subsequent images are lazy loaded.

For section background images:

{% if section.settings.background_image %}
  <img
    src="{{ section.settings.background_image | image_url: width: 1400, format: 'webp' }}"
    loading="lazy"
    width="1400"
    height="600"
    alt="{{ section.settings.background_image.alt | escape }}"
    class="section-bg-image"
  >
{% endif %}
Always include width and height. Without them, the browser cannot calculate the aspect ratio and reserves no space while the image loads, causing the layout to shift when the image arrives. For Shopify images, use the object's built-in properties: width="{{ image.width }}" height="{{ image.height }}"

How to Lazy Load Videos in Shopify

Videos are the heaviest assets on most Shopify homepages. A background video that autoloads on page open adds several megabytes to the initial page weight. Lazy loading video requires a different approach than images.

MP4 Background Videos

Use a poster image (a static frame from the video) as a visible placeholder. Load the video source only when the container enters the viewport using Intersection Observer. Visitors see the poster image instantly. The video loads when the container enters the viewport.

YouTube and Vimeo Embeds

Never embed YouTube or Vimeo with a standard <iframe> if performance matters. Each embed loads several hundred kilobytes of JavaScript before the video plays. Use a facade pattern instead: show a thumbnail image with a play button overlay, and only load the actual iframe when the visitor clicks play. This saves 400 to 600KB of JavaScript per embed.

YouTube facade pattern implementation:

<div class="video-facade" data-video-id="YOUR_VIDEO_ID">
  <img
    src="https://img.youtube.com/vi/YOUR_VIDEO_ID/maxresdefault.jpg"
    loading="lazy"
    width="1280"
    height="720"
    alt="Video thumbnail"
  >
  <button class="play-button" aria-label="Play video">
    <svg><!-- play icon --></svg>
  </button>
</div>
document.querySelectorAll('.video-facade').forEach(facade => {
  facade.querySelector('.play-button').addEventListener('click', function() {
    const videoId = facade.dataset.videoId;
    const iframe = document.createElement('iframe');
    iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
    iframe.allow = 'autoplay; encrypted-media';
    iframe.allowFullscreen = true;
    facade.replaceWith(iframe);
  });
});

How to Lazy Load Shopify Sections

Lazy loading entire sections means deferring the script initialization and heavy content rendering for below-the-fold sections until the visitor is near them. This is more complex than image lazy loading but delivers significant gains for content-heavy homepages.

document.querySelectorAll('[data-lazy-section]').forEach(section => {
  const observer = new IntersectionObserver(function(entries) {
    if (entries[0].isIntersecting) {
      const sectionType = section.dataset.lazySection;
      initSection(sectionType, section);
      observer.unobserve(section);
    }
  }, { rootMargin: '400px 0px' }); // Start loading 400px before visible

  observer.observe(section);
});

function initSection(type, element) {
  switch(type) {
    case 'reviews': loadReviewWidget(element); break;
    case 'instagram': loadInstagramFeed(element); break;
    case 'recommendations': loadProductRecommendations(element); break;
  }
}

In your Liquid section files, add the data-lazy-section attribute and a placeholder to prevent layout shift:

<div data-lazy-section="reviews" class="reviews-section">
  <div class="reviews-placeholder">
    <div class="placeholder-line"></div>
    <div class="placeholder-line"></div>
  </div>
</div>
Match placeholder height to the expected height of the loaded section. This prevents layout shift when the real content loads. A reviews section that renders at 400px tall should have a placeholder approximately 400px tall.

Avoiding SEO Issues with Lazy Loading

Lazy loading done incorrectly can hide content from Google's crawler, which affects indexing and rankings. Done correctly, lazy loading has no negative SEO impact.

Safe for SEO
  • Images using loading="lazy" - Googlebot crawls these
  • Content loaded via Intersection Observer on scroll
  • Lazy-loaded images with descriptive alt text
  • Server-side Liquid rendered text content
Can Cause SEO Issues
  • Lazy-loaded images without alt text
  • Product names and descriptions only appearing after JS initialization
  • Content that only loads after a button click (not scroll)
  • Critical page text hidden behind lazy loading
Test with Google's URL Inspection tool in Search Console to check how Google renders your pages. The rendered HTML view shows you what content Google actually sees after JavaScript execution. If lazy-loaded content appears in the rendered output, it is being indexed. Also use Google's Mobile-Friendly Test as a proxy view of what Googlebot sees.

Fixing Common Lazy Loading Bugs in Shopify

Bug: Images appear as blank areas before loading

Fix: Add a background color or skeleton placeholder to image containers so the load feels progressive rather than broken:

.product-image-container {
  background-color: #f0f0f0;
  aspect-ratio: 4 / 5;
}
Bug: CLS from images without dimensions

Fix: Always include width and height attributes. For Shopify images, use the object's built-in properties: width="{{ image.width }}" height="{{ image.height }}". This lets the browser reserve the correct space before the image downloads.

Bug: LCP image being lazy loaded accidentally

Fix: Audit every <img> tag in your product and homepage templates. Confirm the first visible image uses loading="eager" and fetchpriority="high". Search theme files for loading="lazy" and verify none apply to the hero or main product image.

Bug: Intersection Observer not firing in some browsers

Fix: Add a fallback for older browsers that load all deferred content immediately:

if ('IntersectionObserver' in window) {
  // Use observer
} else {
  // Load all immediately
}

Performance Testing for Lazy Loading

Lazy loading improvements show up in specific metrics. Knowing which metrics to watch helps you validate that your implementation is working correctly.

1
Total page weight reduction
In Chrome DevTools Network tab, look at the total transferred size at the bottom of the panel after the initial page load (before any scrolling). This should decrease significantly after implementing lazy loading. The difference represents resources deferred to load-on-scroll.
2
LCP improvement
LCP should improve because the browser is no longer competing to download off-screen images simultaneously with the LCP element. Fewer concurrent downloads means more bandwidth available for the LCP image.
3
Time to Interactive
Deferring heavy app scripts and section initializations reduces the JavaScript execution burden during the critical load window, typically improving Time to Interactive by 0.5 to 2 seconds on stores with many apps.
4
Before and after with WebPageTest filmstrip
Use WebPageTest.org with filmstrip view. This shows a frame-by-frame screenshot of the page loading. With lazy loading enabled, you will see above-the-fold content appear quickly while below-the-fold content loads progressively as the simulated scroll continues.

Advanced Lazy Loading Techniques for Shopify

Priority Hints for Fine-Grained Control

The fetchpriority attribute allows you to hint to the browser how important each resource is, beyond just eager vs lazy. The first image loads with high priority (LCP). Images 2 to 4 load eagerly but with low priority so they do not compete with the first image. Everything else loads lazily.

Skeleton Screens

Skeleton screens replace blank placeholders with an animated loading state that matches the shape of the incoming content. This is the pattern used by major ecommerce platforms. It signals to visitors that content is coming rather than that the page is broken, reducing bounce rate in the fraction of a second between scroll and load.

Predictive Lazy Loading

Instead of waiting for elements to enter the viewport, use scroll velocity to predict where the visitor is heading and preload content slightly ahead. This is a refinement rather than a foundational fix, appropriate after the core lazy loading implementation is in place.

Skeleton screen CSS implementation:

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

Shopify-Specific Lazy Loading Tips

Use image_url for All Lazy-Loaded Images

Images using Shopify's CDN filter support format conversion and responsive resizing. A lazy-loaded image that is still a 2MB JPEG defeats the purpose. Every lazy-loaded image should use image_url with a width parameter and format: 'webp'.

Test Across All Shopify Page Templates

Shopify has distinct page templates: index, product, collection, article, page, cart. Your lazy loading implementation may work on collection pages but have edge cases on article or search result pages. Test each template separately.

Watch for App Conflicts

Some Shopify apps initialize immediately and modify image elements, removing lazy loading attributes in the process. After implementing lazy loading, inspect your product images in DevTools to confirm the loading="lazy" attribute is still present on the rendered HTML.

Combine lazy loading with image optimization for compound gains. A lazy-loaded image that is still 500KB in size is better than an eager-loaded 500KB image, but not by enough. The full optimization is lazy loading plus WebP conversion plus correct sizing. Ecom: Page Speed Expert handles image optimization at the Shopify store level, which pairs directly with the lazy loading implementation covered in this guide.

Best Practices Checklist

Image Lazy Loading

  • Load the first visible row of images eagerly
  • Use loading="lazy" as the default for all below-fold images
  • Always pair with explicit width and height attributes
  • Use fetchpriority="high" on the LCP image
  • Use image_url with WebP format on every image

Scripts and Sections

  • Use Intersection Observer for scripts, video, and section initializations
  • Set rootMargin: '300px' to preload before the visitor arrives
  • Add placeholder content to prevent layout shift
  • Use YouTube facade pattern for video embeds
  • Add Intersection Observer fallback for older browsers

Testing and Maintenance

  • Test in PageSpeed Insights and DevTools Network tab
  • Check Google Search Console to confirm lazy-loaded content is indexed
  • Retest after every theme update and app installation
  • Inspect rendered HTML to confirm lazy attributes survive app initialization
  • Use WebPageTest filmstrip view to validate progressive loading

Summary

Lazy loading is one of the highest return-on-effort optimizations available for Shopify stores. The implementation is straightforward, the browser support is excellent, and the performance gains are immediate and measurable.

Use native loading="lazy" for images and iframes. Use Intersection Observer for scripts, videos, and section initializations. Never lazy load above-the-fold content. Always include image dimensions. Test in DevTools to confirm resources are actually being deferred, and check Search Console to confirm Google is still indexing your content.

Pair lazy loading with WebP image compression and correct image sizing - available through tools like Ecom: Page Speed Expert - and the compound effect on your PageSpeed scores and real user experience is significant. Each optimization layer reinforces the others. Lazy loading is the one that makes everything else more effective.
Retour au blog

Frequently Asked Questions

Browser support for native lazy loading exceeds 95 percent globally. Safari added full support in Safari 15. For the small percentage of visitors on older browsers, images without lazy loading support simply load immediately, the same behavior as before lazy loading was implemented. There is no broken experience for unsupported browsers.

Poor implementation can. If your product images below the first row load visibly late, with blank space appearing and then filling in as the visitor scrolls, the experience feels broken. Use appropriate placeholder backgrounds and generous root margins (300 to 500px) so images load before the visitor reaches them. Correct implementation is invisible to the visitor.

No. Lazy loading your LCP image delays the most important resource on the page and directly worsens your PageSpeed score. Lazy loading your main navigation scripts can break interactions. The rule is: above the fold, eager load; below the fold, lazy load.

Correctly implemented lazy loading has no negative SEO effect. Googlebot renders JavaScript and crawls lazy-loaded content. Every lazy-loaded image should still include descriptive alt text for image indexing. Use URL Inspection in Search Console to verify Google is seeing your lazy-loaded content in the rendered HTML.

They solve different problems. loading="lazy" is a browser attribute that defers image and iframe downloads. Intersection Observer is a JavaScript API for detecting when elements enter the viewport. Use the attribute for images, it is simpler and requires no JavaScript. Use Intersection Observer for script initialization, section loading, and video handling where the attribute is not applicable.