DEV Community

Cover image for Lazy load an iframe
Phuoc Nguyen
Phuoc Nguyen

Posted on • Originally published at phuoc.ng

Lazy load an iframe

In our previous posts, we learned how to use the Intersection Observer API to lazy load images and background images. This powerful tool allows us to detect when an element enters the viewport and take action accordingly, such as loading resources on demand.

Now, let's talk about iframes. They're commonly used to embed external content, like videos, maps, or ads. But iframes can be slow and resource-intensive, especially if they contain large or complex content. That's where lazy loading comes in. By deferring the loading of iframes until they're actually needed, you can improve your page's performance and user experience.

In this post, we'll dive into how to use the Intersection Observer API to lazy load iframes.

Setting up an iframe for lazy loading

Setting up lazy loading for an iframe is similar to lazy loading images. Instead of setting the src attribute directly, we'll use a custom data attribute like data-src. This way, the iframe won't load immediately when the page loads.

<iframe
    ref={iframeRef}
    data-src="https://path/to/iframe"
/>
Enter fullscreen mode Exit fullscreen mode

We've added a ref to the iframe, which we'll use to observe it with the IntersectionObserver.

const iframeRef = React.useRef<HTMLIFrameElement>(null);
Enter fullscreen mode Exit fullscreen mode

Setting up the IntersectionObserver

In the next step, we'll set up an IntersectionObserver to watch the iframe and detect when it comes into view. Once the iframe becomes visible, we'll grab the URL from the data-src attribute and use it to set the src attribute, which will start loading the iframe.

Here's a code snippet that uses the useEffect hook to create an instance of the IntersectionObserver:

React.useEffect(() => {
    const iframe = iframeRef.current;

    const observer = new IntersectionObserver(([entry]) => {
        if (entry.isIntersecting) {
            // Load the iframe when it's visible
            iframe.src = iframe.dataset.src;
            observer.unobserve(iframe);
        }
    }, {
        threshold: 0,
    });

    observer.observe(iframe);

    return () => {
        observer.disconnect();
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

In this code snippet, we're creating an Intersection Observer that watches an element (in this case, an iframe) to see if it's visible in the viewport. If it is, we grab the URL from the data-src attribute using iframe.dataset.src, and use it to set the src attribute of the iframe. Then, we stop observing the iframe since it's already been loaded.

Check out the demo below and see how it works by scrolling down to the bottom.

Video credit: Glacier express by @xat-ch

Improving user experience with loading indicators

To enhance the user experience, it's essential to display a loading indicator or placeholder while an iframe is loading. You can easily achieve this by adding a loading class to the iframe, which will display a loading indicator as a background image. This creates a placeholder where the iframe will be displayed.

<iframe data-src="..." className="loading"></iframe>
Enter fullscreen mode Exit fullscreen mode

To define the loading class in CSS, we set its background property to an image file that contains a loading indicator. We use center center no-repeat to position the image at the center of the iframe and prevent it from repeating.

.loading {
    background: url('/path/to/loading.svg') center center no-repeat;
}
Enter fullscreen mode Exit fullscreen mode

After the iframe finishes loading, you can remove the loading class and display its content instead of the placeholder. To achieve this, we simply add an event listener for the load event on the iframe.

const handleLoadFrame = () => {
    const iframe = iframeRef.current;
    if (iframe) {
        iframe.classList.remove('loading');
    }
};

// Render
<iframe onLoad={handleLoadFrame} />
Enter fullscreen mode Exit fullscreen mode

If you're not interested in using a background image for the loading indicator, there's another option. We can use state variables to keep track of when the iframe is loading and when it's loaded.

To do this, we'll use an enum called Status with three possible values: NotLoaded, Loading, and Loaded. We'll also create a state variable called status to keep track of the current status of the iframe.

enum Status {
    NotLoaded,
    Loading,
    Loaded,
}

const [status, setStatus] = React.useState(Status.NotLoaded);
Enter fullscreen mode Exit fullscreen mode

When the iframe is first displayed, it's set to NotLoaded. As soon as it begins to load, we change the status to Loading.

const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
        if (entry.isIntersecting) {
            setStatus(Status.Loading);
        }
    });
}, {
    threshold: 0,
});
Enter fullscreen mode Exit fullscreen mode

Once the iframe finishes loading, we update the status to Loaded. To achieve this, we can add an event listener for the load event on the iframe. When this event triggers, we set the status to Loaded.

const handleLoadFrame = () => {
    setStatus(Status.Loaded);
};

// Render
<iframe onLoad={handleLoadFrame} />
Enter fullscreen mode Exit fullscreen mode

Using this setup, we can choose to display different components based on the current status of the iframe. For instance, while the iframe is loading, we could show a spinner or progress bar. Once it's completely loaded, we could then display the content of the iframe.

<div className="container">
    {status === Status.Loading && (
        <div className="loading">Loading ...</div>
    )}
    <iframe ... />
</div>
Enter fullscreen mode Exit fullscreen mode

That's it! We've successfully created an iframe that only loads when it appears on the screen. This can significantly improve the performance of pages with multiple iframes.

Take a look at the demo below to see it in action:

Conclusion

Using the Intersection Observer API to lazy load iframes is an easy and effective way to boost your page's performance. By delaying the loading of iframes until they're actually visible in the viewport, you can speed up your page's initial load time and make for a smoother user experience. Just make sure you handle the loading states properly so your users know what's happening.


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

Top comments (0)