Back to blog

Lazy Loading Video on the Web: MP4 vs HLS

Lazy loading images is way easier...

When you’re dealing with a page with lots of assets, you have to think about performance. Loading everything up front will tank your Largest Contentful Paint (LCP) score and wreck your user experience. The solution, as you probably know, is lazy loading.

Lazy loading images is easy these days — native support with loading="lazy" works great for <img> and <iframe>. But video? Not so simple.

Try putting loading="lazy" on a <video> tag — nothing happens. But we still want to lazy load videos, and there are ways to do it.


The Old-School Way: data-src

This method has been around for ages.

<video data-src="video.mp4" muted playsinline></video>

Then in your JavaScript you utilize an IntersectionObserver to check when the video is intersecting - when it's in the viewport - and set the actual src tag:

video.src = video.dataset.src; 
video.load();

Done.

Caveat: if you don’t include a poster image, you’ll get layout thrashing and a flash of empty space — not great UX.


The Modern Way: preload="none" and load()

A cleaner, more modern approach is to use the preload attribute.

<video preload="none" src="video.mp4" poster="thumb.jpg" muted playsinline></video>

Awesome, now what? Again, use an IntersectionObserver and when the video is intersecting, instead of setting the video src simply load it.

video.load();

Much cleaner. This method works great for MP4s. Use preload="metadata" if you need basic info like duration or dimensions before loading. Just don’t use auto — that defeats the purpose of lazy loading.


What About HLS?

HLS (HTTP Live Streaming) is a format introduced by Apple in 2009. It uses adaptive bitrate streaming — basically, it detects the user’s bandwidth and CPU in real time and adjusts quality on the fly. HLS streams end in .m3u8.

Here’s the catch: only Safari supports HLS natively. Chrome and Firefox don’t — they need a JavaScript player like hls.js.

Now it's a bit trickier.

If you try to use the <video> tag with an .m3u8 src and preload="none", it might work in Safari, but Chrome and Firefox will ignore it entirely — stripping out the HLS reference.


Lazy Loading HLS: The Right Way

Instead of relying on <video src="..." preload="none">, you create a video tag without a src, and attach HLS playback manually in JavaScript:

const video = document.querySelector('video');
const hls = new Hls({ autoStartLoad: false });

const observer = new IntersectionObserver(([entry]) => {
  if (entry.isIntersecting) {
    hls.loadSource('video.m3u8');
    hls.attachMedia(video);
    hls.startLoad(); // optional
    observer.disconnect();
  }
});

observer.observe(video);

Hurray! Lazy-loaded HLS playback on all major browsers.

Here's.a link to all the methods you can use with the hls library.


When to Use MP4 vs. HLS


That’s the basic gist of it. Lazy loading video is totally doable — just depends on what format you’re working with and how much complexity you’re willing to take on.