How to Lazy Load Videos in Shopify (2026)

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

Key takeaways

  • Native video elements should use loading="lazy" and preload="none" to prevent auto-downloading video data.
  • YouTube and Vimeo embeds use a “facade” pattern — show a clickable thumbnail, load the iframe only when clicked.
  • Background videos should load only when the section scrolls into view, not on page load.
  • Unoptimized video is one of the largest contributors to slow Shopify page load times.

Video on a Shopify store can be compelling — product demos, brand films, how-to content. But video is also one of the heaviest assets on the web. A single unoptimized video embed can add seconds to your page load time even for visitors who scroll past it without watching.

Lazy loading video is the fix.

How to add lazy loading to native video in Shopify

For self-hosted video files uploaded to Shopify’s asset system or a CDN, use HTML’s native lazy loading attributes.

<video
  src="{{ section.settings.video_url }}"
  poster="{{ section.settings.poster_image | img_url: '1200x' }}"
  loading="lazy"
  preload="none"
  muted
  playsinline
  loop
>
  Your browser doesn't support video.
</video>

Key attributes:

loading="lazy" — tells the browser to defer loading the video source until needed.

preload="none" — prevents the browser from downloading any video data until the user interacts with the player. Without this, browsers often prefetch video metadata or even the first few seconds of video, wasting bandwidth.

poster — shows a still image while the video hasn’t loaded. Always include this. Without a poster, visitors see a blank gray rectangle until the video loads. The poster image should be pre-loaded (don’t lazy load it).


The facade pattern for YouTube and Vimeo embeds

YouTube and Vimeo iframes are among the biggest performance problems on Shopify stores. Each one loads multiple megabytes of scripts from YouTube/Vimeo’s servers, even if the visitor never watches the video.

The facade pattern: replace the iframe with a clickable thumbnail image. When the visitor clicks to play, replace the image with the actual iframe.

Here’s a basic implementation:

<div class="video-facade" data-video-id="YOUR_YOUTUBE_ID">
  <img
    src="https://img.youtube.com/vi/YOUR_YOUTUBE_ID/maxresdefault.jpg"
    alt="Video thumbnail"
    loading="lazy"
    style="width:100%; cursor:pointer;"
  />
  <button class="video-play-btn" aria-label="Play video">
    <!-- Play icon SVG here -->
  </button>
</div>
document.querySelectorAll('.video-facade').forEach(function(facade) {
  facade.addEventListener('click', function() {
    var videoId = facade.dataset.videoId;
    var iframe = document.createElement('iframe');
    iframe.src = 'https://www.youtube.com/embed/' + videoId + '?autoplay=1';
    iframe.allow = 'autoplay; encrypted-media';
    iframe.style.width = '100%';
    iframe.style.height = '100%';
    facade.replaceWith(iframe);
  });
});

For Vimeo: replace img.youtube.com/vi/ID/maxresdefault.jpg with a Vimeo thumbnail URL and update the embed URL to https://player.vimeo.com/video/ID?autoplay=1.

Easier option: Use the Lite YouTube Embed web component (a popular open-source library) which implements the facade pattern with a single HTML element.


Lazy loading background videos

Background videos (the kind that auto-play as a decorative element behind text) are particularly wasteful if they load for every visitor who lands on the page.

Approach: load video only when the section scrolls into view.

var videoSection = document.querySelector('.hero-video-section');
var videoEl = videoSection.querySelector('video');

var observer = new IntersectionObserver(function(entries) {
  entries.forEach(function(entry) {
    if (entry.isIntersecting) {
      // Load the video
      var source = videoEl.querySelector('source');
      if (source && source.dataset.src) {
        source.src = source.dataset.src;
        videoEl.load();
        videoEl.play();
      }
      observer.disconnect();
    }
  });
}, { threshold: 0.1 });

observer.observe(videoSection);

Use this with a <source data-src="..."> instead of <source src="..."> to prevent the browser from downloading the video source before the Intersection Observer fires.

For the hero section (above the fold): background video in the hero loads immediately regardless — it’s already visible. In this case, use preload="metadata" instead of preload="none" so the browser prepares to play the video quickly.

Build fast Shopify video sections by describing them to Fudge.
Try Fudge for Free

Should you use video in Shopify at all?

Video is worth it when:

Video is not worth it when:

Consider GIF alternatives. For short looping clips (2-5 seconds), an MP4 video with autoplay muted loop is typically 60-80% smaller than an equivalent GIF while looking identical.


Checking video performance impact

Run your product or homepage URL through PageSpeed Insights. Video embeds will often trigger:

After implementing the facade pattern, re-run PageSpeed — you should see improvements in both score and Total Blocking Time.

Jacques's signature
Build fast Shopify sections with proper video handling by describing them.

You might also be interested in

How to Fix Render-Blocking Scripts in Shopify (2026)
Fix render-blocking scripts in Shopify — add defer and async to script tags, use Shopify's section JavaScript blocks, and move non-critical scripts
How to Speed Up a Shopify Theme (2026)
Speed up your Shopify store — fix slow Shopify themes with image compression, lazy loading, removing unused apps, and render-blocking script fixes
How to Lazy Load Images in Shopify (2026)
Add lazy loading to images in Shopify — check if your theme includes loading='lazy' by default, how to add it manually, and why hero images should