Shopify Lazy Loading Guide: How to Implement Lazy Loading for Images and Videos

Lazy loading can reduce initial page weight by 60–80% on image-heavy Shopify collection pages. By deferring the loading of below-the-fold images and videos until users scroll near them, you dramatically improve initial load times, reduce bandwidth consumption, and boost Core Web Vitals scores — particularly LCP and overall page speed.

What Is Lazy Loading and How Does It Work?

Lazy loading is a technique that delays the loading of non-critical resources until they are needed. Instead of downloading all 48 product images on a collection page immediately, lazy loading loads only the images visible in the viewport (above the fold) and defers the rest until the user scrolls near them.

This approach delivers three major benefits for Shopify stores. First, it reduces the initial data transferred, making pages load faster on any connection speed. Second, it conserves bandwidth for mobile users on cellular networks. Third, it reduces the browser's rendering workload by processing fewer images during the initial page load, improving responsiveness.

The technique relies on detecting when an image is about to enter the viewport. Once the browser determines the image is within a certain distance of the visible area, it begins downloading the image. By the time the user scrolls to it, the image is already loaded or nearly loaded, making the experience seamless.

Native Lazy Loading vs. JavaScript Lazy Loading

There are two primary approaches to implementing lazy loading on Shopify: the native HTML loading attribute and JavaScript-based solutions using the Intersection Observer API.

Native Lazy Loading

The simplest approach is the native HTML loading="lazy" attribute, supported by all modern browsers (Chrome, Firefox, Edge, Safari 15.4+):

<img src="{{ image | image_url: width: 600 }}" loading="lazy" width="600" height="400" alt="{{ image.alt }}">

Advantages:

Limitations:

JavaScript Lazy Loading (Intersection Observer)

For more control, use the Intersection Observer API to detect when elements approach the viewport:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { rootMargin: '200px' });

Advantages:

Limitations:

Comparison Table

FeatureNative loading="lazy"Intersection Observer
JavaScript requiredNoYes
Browser support95%+ (all modern browsers)95%+ (all modern browsers)
Custom thresholdNoYes (rootMargin)
Placeholder supportNoYes
Background imagesNoYes
Video supportiframes onlyAny element
SEO safeYesYes (with noscript fallback)
Implementation effortMinimalModerate

Recommendation: Use native lazy loading as your default. For 90% of Shopify stores, native loading="lazy" provides excellent results with zero maintenance. Reserve JavaScript-based lazy loading for specific use cases like background images, video embeds, or when you need custom placeholder animations.

Implementing Lazy Loading in Shopify Liquid Templates

Product Image Galleries

On product pages, eagerly load the main (first) image and lazy load all additional gallery images. In your product template:

{% for image in product.images %}
  {% if forloop.first %}
    <img src="{{ image | image_url: width: 800 }}" width="{{ image.width }}" height="{{ image.height }}" alt="{{ image.alt | escape }}" fetchpriority="high">
  {% else %}
    <img src="{{ image | image_url: width: 800 }}" loading="lazy" width="{{ image.width }}" height="{{ image.height }}" alt="{{ image.alt | escape }}">
  {% endif %}
{% endfor %}

The first image loads immediately with fetchpriority="high" because it is the LCP element. All subsequent images use loading="lazy" to defer loading until the user scrolls to them.

Collection Pages

Collection pages benefit the most from lazy loading because they contain dozens of product images. A practical approach is to eagerly load the first row of products (typically 3–4 images) and lazy load the rest:

{% for product in collection.products %}
  {% if forloop.index <= 4 %}
    <img src="{{ product.featured_image | image_url: width: 400 }}" width="{{ product.featured_image.width }}" height="{{ product.featured_image.height }}" alt="{{ product.featured_image.alt | escape }}">
  {% else %}
    <img src="{{ product.featured_image | image_url: width: 400 }}" loading="lazy" width="{{ product.featured_image.width }}" height="{{ product.featured_image.height }}" alt="{{ product.featured_image.alt | escape }}">
  {% endif %}
{% endfor %}

Blog Posts and Content Pages

For blog posts with inline images, add loading="lazy" to all images except the first one (which may be the hero/featured image). If your blog content is entered via the rich text editor, you may need to add lazy loading via JavaScript since you cannot control the HTML output directly.

Lazy Loading Videos on Shopify

YouTube and Vimeo Embeds

Embedded videos from YouTube and Vimeo use iframes, which can add 500 KB or more to page weight. Use native lazy loading on iframes:

<iframe src="https://www.youtube.com/embed/VIDEO_ID" loading="lazy" width="560" height="315" title="Product demo video" allowfullscreen></iframe>

For even better performance, use the lite-youtube-embed approach: display a static thumbnail image that looks like a video player. Only load the actual YouTube iframe when the user clicks play. This saves 500+ KB per embed on initial load.

Self-Hosted Videos

For self-hosted MP4 videos, avoid autoplay on below-the-fold videos. Instead, use preload="none" to prevent the browser from downloading any video data until the user initiates playback:

<video preload="none" poster="/path/to/thumbnail.jpg" controls width="640" height="360">
  <source src="/path/to/video.mp4" type="video/mp4">
</video>

The poster attribute provides a lightweight placeholder image while preload="none" ensures the video data is not downloaded until play is clicked.

Avoiding Common Lazy Loading Mistakes

Mistake 1: Lazy Loading the LCP Image

Never lazy load the largest contentful paint element. This is the number one lazy loading mistake on Shopify stores. Identify which image is your LCP element (usually the hero image or main product image) and ensure it loads eagerly with fetchpriority="high".

Mistake 2: Missing Width and Height Attributes

Lazy-loaded images without explicit dimensions cause Cumulative Layout Shift (CLS). When the image finally loads, it pushes surrounding content around. Always include width and height attributes or use CSS aspect-ratio on the container.

Mistake 3: Lazy Loading All Images

Some themes and apps apply lazy loading to every image on the page, including above-the-fold images. This hurts LCP because the browser delays loading visible content. Only lazy load images that are below the fold on initial page load.

Mistake 4: No Fallback for JavaScript Lazy Loading

If you use JavaScript-based lazy loading, provide a <noscript> fallback with regular image tags. While rare, some users have JavaScript disabled, and search engine crawlers may not always execute JavaScript:

<noscript><img src="{{ image | image_url: width: 600 }}" alt="{{ image.alt }}"></noscript>

Mistake 5: Using Large Placeholder Images

If you use JavaScript lazy loading with placeholder images, ensure the placeholders are tiny (under 1 KB). A common approach is to use a 20px-wide blurred version of the actual image encoded as a base64 data URI, which provides a visual preview with negligible data cost.

Measuring the Impact of Lazy Loading

After implementing lazy loading, measure the impact using these methods:

EA Page Speed Booster for Automated Lazy Loading

Implementing lazy loading manually across an entire Shopify store can be tedious, especially when dealing with multiple templates, app-injected images, and blog content. EA Page Speed Booster automates the process by intelligently applying lazy loading to below-the-fold images across your entire store while preserving eager loading for LCP elements. The app also handles image compression, format optimization, and dimension management, addressing the most common lazy loading pitfalls without manual code changes.

Advanced Lazy Loading Techniques

Progressive Image Loading (Blur-Up)

The blur-up technique loads a tiny, heavily compressed version of the image first (often 20–40 pixels wide), displays it blurred and scaled up as a placeholder, then swaps in the full-resolution image when it loads. This provides immediate visual feedback while keeping initial data transfer minimal. Shopify's Dawn theme uses a similar approach with its image loading animation.

Priority Hints

Combine lazy loading with the fetchpriority attribute for precise control over resource loading priority. Use fetchpriority="high" on LCP images and fetchpriority="low" on images that are less important even when visible:

<img src="hero.jpg" fetchpriority="high"> <!-- LCP image -->
<img src="badge.png" fetchpriority="low"> <!-- Decorative, above-fold but not LCP -->
<img src="product-5.jpg" loading="lazy"> <!-- Below fold -->

Connection-Aware Loading

Use the Network Information API to adjust lazy loading behavior based on the user's connection speed. On slow connections (2G, slow 3G), you might load even smaller image variants or defer more aggressively:

if (navigator.connection && navigator.connection.effectiveType === '2g') {
  // Use smaller image variants or more aggressive deferral
}

Related Guides

Frequently Asked Questions

Does lazy loading hurt SEO for Shopify stores?

No, lazy loading does not hurt SEO when implemented correctly. Googlebot renders JavaScript and can discover lazy-loaded images. Using the native loading="lazy" attribute is fully supported by Google. However, never lazy load above-the-fold images (especially the LCP element) as this delays the largest contentful paint and can harm both rankings and user experience.

Should I lazy load the main product image on Shopify?

No. The main product image is typically the LCP element on product pages. Lazy loading it would delay LCP and hurt your Core Web Vitals score. Instead, load the main product image eagerly with fetchpriority="high" and preload it in the head. Only lazy load additional product images, thumbnail galleries, and below-the-fold content.

What is the difference between native and JavaScript lazy loading?

Native lazy loading uses the HTML loading="lazy" attribute and is handled entirely by the browser with zero JavaScript overhead. JavaScript lazy loading uses the Intersection Observer API to load images when they enter the viewport. Native is simpler and more performant but offers less control. JavaScript approaches allow custom placeholders, fade-in effects, and precise viewport distance triggers.

Does lazy loading cause layout shifts (CLS)?

Lazy loading can cause CLS if images do not have explicit width and height attributes or CSS aspect-ratio set. When a lazy-loaded image loads, if no space was reserved for it, surrounding content shifts to accommodate it. Always set dimensions on lazy-loaded images to prevent layout shifts.

How much does lazy loading improve Shopify page speed?

The impact depends on how many images are on the page. On a collection page with 48 products, lazy loading can reduce initial page weight by 60–80% and cut load time by 2–4 seconds on mobile connections. On a product page with a gallery of 8 images, the savings are typically 30–50% reduction in initial image bytes transferred.

Automate Lazy Loading on Your Shopify Store

EA Page Speed Booster applies intelligent lazy loading, image compression, and format optimization across your entire store — no code changes required.

Install Free on Shopify