DEV Community

Cover image for Lazy load a video
Phuoc Nguyen
Phuoc Nguyen

Posted on • Updated on • Originally published at phuoc.ng

Lazy load a video

Lazy loading is an awesome technique that delays the loading of non-essential resources, such as images and videos, until they're actually needed. This can make a huge difference in how quickly a website loads, especially for pages with a lot of media content.

In one of our previous posts, we showed you how to lazy load images using the IntersectionObserver API. In this post, we'll take it a step further and show you how to use the same technique to lazy load videos too.

Using IntersectionObserver to detect when a video is visible

The IntersectionObserver API is a powerful tool that lets you know when an element enters or leaves the viewport. This is particularly useful for lazy loading because we only want to load a video when the user can actually see it.

Here's the code snippet that shows how we can do it:

const videoRef = React.useRef<HTMLVideoElement>(null);

React.useEffect(() => {
    const video = videoRef.current;
    if (!video) {
        return;
    }

    const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                video.src = video.dataset.src;
                observer.unobserve(video);
            }
        });
    }, {
        threshold: 0,
    });

    observer.observe(video);

    return () => {
        observer.unobserve(video);
    };
}, []);

return (
    <video
        ref={videoRef}
        data-src="/path/to/video.mp4"
        controls
    />
);
Enter fullscreen mode Exit fullscreen mode

In this example, we create a reference called videoRef and attach it to the <video> element. Then, we use a useEffect hook to create a new instance of IntersectionObserver that will watch the video element.

When the video becomes visible (i.e., it intersects with the viewport), the observer callback is triggered. Inside the callback, we set the src attribute of the video to the value of the data-src attribute, which contains the actual video URL.

To prevent the video from being loaded again if it leaves and re-enters the viewport, we unobserve the video element.

Check out the demo below to see it in action!

Video credit: City traffic by @xat-ch

Displaying a loading indicator

It's always a good idea to show a loading indicator while the video is loading. This lets the user know that something is happening. You can easily achieve this by conditionally rendering a loading spinner based on the video's loading state.

const [isLoading, setIsLoading] = React.useState(true);

const handleLoadedData = () => {
    setIsLoading(false);
};

return (
    <div className="container">
        {isLoading && <div className="loading">Loading...</div>}
        <video
            ref={videoRef}
            data-src="/path/to/video.mp4"
            onLoadedData={handleLoadedData}
            controls
        />
    </div>
);
Enter fullscreen mode Exit fullscreen mode

To improve the user experience, we can add a isLoading state variable that starts out as true. We also attach an onLoadedData event handler to the video that sets isLoading to false once the video metadata has loaded.

To visually indicate to the user that the video is loading, we conditionally render a loading indicator element in the JSX if isLoading is true. This loading indicator can be any kind of spinner or placeholder you want.

Check out the demo below. Scroll down to the bottom to see the loading indicator appear before the video is completely loaded.

Conclusion

One way to improve your website's performance is by using IntersectionObserver to lazy load videos. This means that the videos won't load until the user can see them, which reduces page load time and saves bandwidth.

Don't forget to add a loading indicator when you implement lazy loading. This will let users know that content is coming and improve their experience on your website.


If you want more helpful content like this, feel free to follow me:

Top comments (0)