Fixes · Page speed and video

Store slow on phones: videos you can’t see are still downloading

The fix

A background video autoplays, and an autoplaying video downloads whether or not anyone can see it: a desktop copy hidden on phones, one in a menu that hasn’t been opened, one behind an image the editor chose instead.

Give each video no source until it’s on screen, or until its menu opens, then attach it. Shopify’s video_tag now marks videos lower down the page as lazy, but Safari and Firefox ignore that, so don’t rely on it.

01Why hidden videos still download

On this Shopify Community thread (Jan 2024), a merchant’s video banners were slow to appear on every page; the reply suggested a poster image. A poster covers the wait, but it doesn’t make the download any smaller. Three things make a page heavier than it looks:

  1. 01

    Autoplay outranks preload

    MDN: “The autoplay attribute has precedence over preload. If autoplay is specified, the browser would obviously need to start downloading the video for playback” (MDN, video). Background videos are almost always autoplaying, muted and looped, so preload="none" on its own doesn’t hold them back.

  2. 02

    Hiding a video doesn’t stop it

    Themes often draw one video for desktop and another for phones, and hide the one that doesn’t fit with CSS. On the stores we measured, the hidden copy downloaded as well. A menu panel that’s closed but still laid out on the page (some themes hide panels without display: none) downloads its video on every page view.

  3. 03

    The setting says Image, the code says video

    A section with a “Media type” choice should stop drawing the video when you pick Image. If its code only checks whether a video file is attached, the image shows and the video downloads behind it. That’s why switching a section to an image can make no difference to speed.

What Shopify does about it today

Shopify’s video_tag filter now adds loading="lazy" itself when you don’t pass a loading value, “for videos in sections further down the page” (Shopify, video_tag). MDN says a lazy video “will not start downloading and autoplaying until the element is near or within the viewport”. That helps, with two limits:

  • Browser support is narrow. As of October 2026, Can I use lists loading="lazy" on video in Chrome and Edge from version 148, and not at all in Safari, on a Mac or an iPhone, or in Firefox (Can I use). On an iPhone, those videos download as they did before.
  • It only covers videos further down the page. A hidden desktop copy of the hero, or a video in the header’s menu, is at the top.

02Weigh the page, then hold back each video

For store owners: weigh a page on a phone

  1. Open the page with Chrome’s developer tools

    In Chrome on a computer, open your homepage, then View > Developer > Developer Tools and the Network tab.

  2. Load it as a phone

    Click the device icon to show the page at phone size, tick Disable cache, set the throttling menu to Fast 4G, and reload. The bar at the bottom shows how much was transferred.

  3. Look for video

    Filter the list by Media. Without scrolling or opening anything, you should see no .mp4 you can’t see on screen. Repeat on a busy product page.

  4. Switch a section to Image and weigh it again

    If the page gets no lighter, the section is still loading the video behind the image. Send this page to your developer.

For developers: no source until it’s seen

Draw the video with its source held back, and attach it when the video comes into view. A copy hidden with display: none never comes into view, so it never loads. This is the shape of what we shipped, generalised:

The video, with its source held back (Liquid)liquid
{%- liquid
  assign mp4 = video.sources | where: 'format', 'mp4' | sort: 'height' | last
-%}
<video
  data-lazy-video
  muted loop playsinline
  preload="none"
  {% if video.preview_image %}
    poster="{{ video.preview_image | image_url: width: 1200 }}"
    width="{{ video.preview_image.width }}"
    height="{{ video.preview_image.height }}"
  {% endif %}
>
  <source data-src="{{ mp4.url }}" type="video/mp4">
</video>
Attach it when it’s in view, pause it when it leavesjs
const observer = new IntersectionObserver((entries) => {
  entries.forEach(({ target: video, isIntersecting }) => {
    if (isIntersecting) {
      const source = video.querySelector('source[data-src]');
      if (source) {
        source.src = source.dataset.src;
        source.removeAttribute('data-src');
        video.load();
      }
      video.play().catch(() => {});
    } else if (!video.paused) {
      video.pause();
    }
  });
}, { rootMargin: '200px 0px' });

document.querySelectorAll('video[data-lazy-video]').forEach((video) => observer.observe(video));
  • For a small thumbnail, take the smallest encoding instead: sort: 'height' | first.
  • A video inside a menu or a pop-up: don’t observe it. Attach its source in the code that opens the panel. A closed panel that still has a size on the page can count as in view.
  • Honour the editor’s choice. Draw the video only when it’s chosen, for example {% if section.settings.video != blank and section.settings.media_type != 'image' %}, using your section’s own setting names.
  • Above the fold, show the video’s first frame as an image with fetchpriority="high" and let the video take over once it plays. Shopify’s performance team describes the same pattern, using each video’s preview_image (Shopify Performance, Feb 2026).
  • If desktop and phone use different files, one video with a media query on each source fetches only the one that fits, in place of two videos and CSS. The same Shopify post does this, with media="(max-width: 749px)" on the phone source and media="(min-width: 750px)" on the desktop one.
  • With JavaScript off, the poster stays, which is a fair fallback for a background video.

03Weigh it again, then scroll

  1. Weigh the page again

    Same steps as above. On load, the only videos in the Media list should be ones on screen.

  2. Scroll, and open the menu

    Each video should start as it comes into view, and its .mp4 should appear in the list only then.

  3. Test in Safari

    The fix shouldn’t depend on anything Safari ignores. On an iPhone, the page should look the same as before, with the videos playing as you reach them.

From our work

04From 37.5MB to 3.9MB on Fuzzball

Fuzzball, a cat food subscription we’ve worked with since 2025, told us the site had slowed down, and that taking video off the new product pages had made no difference. We measured a wet food product page on a phone, throttled to Fast 4G: 37.5MB, 29.7MB of it video, with the main content drawn after 8.1 seconds (Largest Contentful Paint, 10 Aug 2026). There were three causes:

  • The header’s menu autoplayed a 13.5MB, 1080p video on every page of the site, drawn at 160 by 93 pixels. It was in the page twice, and each copy made its own request: roughly 25MB a page.
  • The section’s “Media type” setting was ignored. Choosing Image hid the video but left it downloading, which is why taking video off made no difference.
  • Three product pages still had a 4.5MB video in one section. Those now show each product’s own photo.

After (10 Aug 2026). The same page loads 3.9MB with no video on load, and the main content is drawn after 5.6 seconds, measured the same way, on the same domain with the same throttling. The menu video gets its source when the menu opens; the small menu thumbnail on phones starts when it’s on screen. On the live store today (23 Oct 2026), the menu video still loads with no source. It is still the 1080p file, so opening the menu costs the full download: a smaller upload is the next step.

On a wellness brand’s new theme we found the same pattern: a phone downloaded the hidden desktop hero as well as its own, and every video further down the page on arrival, about 46MB on the homepage in our speed test (7 Oct 2026). Each video now waits until it’s in view and pauses once it has scrolled away. The hero went further the same day: its first frame now shows as a high-priority image, and the video starts once the page has loaded. Chrome then counted the main content as drawn after 1.8 seconds on a phone, from 3.4, and 0.9 seconds on desktop, from 1.6.

The bigger picture

05The heaviest thing on most stores

Video is the heaviest thing most stores carry, and it hides well: the page looks the same whether a hidden copy downloads or not. If your store has slowed down and nobody can say why, weigh a page on a phone before changing anything else.

Measure first, fix, then measure again the same way: that’s how the Fuzzball numbers above were taken.

06Sources

Every step and claim above comes from one of these, or from the fix on the store it describes.

  1. MDN: The video element (autoplay, preload, loading)
  2. Shopify developers: Liquid filters, video_tag
  3. Can I use: Lazy loading via attribute for video and audio
  4. Shopify Performance: Fast video banners that work on every device (Feb 2026)
  5. Shopify Community: Video background as a banner slow to charge (Jan 2024)

Sources checked on .

Contact

Store slow on phones?

Send us the page that feels slowest.