DEV Community

Cover image for Lazy load a Google Map
Phuoc Nguyen
Phuoc Nguyen

Posted on • Originally published at phuoc.ng

Lazy load a Google Map

Google Maps is a powerful tool to add interactive maps to your website. However, embedding a Google Map can negatively affect your site's performance, especially on mobile devices. The map may take a while to load, slowing down your page and frustrating users.

Luckily, we can use lazy loading to defer loading the Google Map until it's actually needed. By using the IntersectionObserver API, we can detect when the map container enters the viewport and only load the map then. This can significantly improve your site's initial load time and performance.

In this post, we'll show you how to lazy load a Google Map in a React component using IntersectionObserver.

Creating the map container

To start, we need to create a container for our Google Map in our React component. By giving the container a ref, we can easily reference it later with IntersectionObserver.

const mapRef = React.useRef<HTMLDivElement>(null);

// Render
return (
    {/* Other content */}
    <div ref={mapRef} />
);
Enter fullscreen mode Exit fullscreen mode

Setting up the IntersectionObserver

Now, we're going to set up an IntersectionObserver to keep an eye on the map container and load the map only when it enters the viewport.

To create an IntersectionObserver, we need to create an instance of the IntersectionObserver class. The constructor takes two arguments: a callback function to execute when the observed element intersects with the viewport, and an options object that specifies additional parameters for the observer.

We also define an options object that sets a visibility threshold of 0. This means that our callback function will be triggered as soon as even a single pixel of our map container is visible within the viewport.

In our case, we want to observe the map container and load the map only when it enters the viewport. To do this, we'll pass a callback function to the constructor that will iterate through each entry in the entries parameter and check if it's intersecting with the viewport using entry.isIntersecting. If it is, we'll load our Google Map and stop observing further changes by calling observer.unobserve(mapEle). This way, we're only loading the map once, and only when it's necessary.

React.useEffect(() => {
    const mapEle = mapRef.current;
    if (!mapEle) {
        return;
    }

    const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                // Load the map ...
                observer.unobserve(mapEle);
            }
        });
    }, {
        threshold: 0,
    });
}, []);
Enter fullscreen mode Exit fullscreen mode

Next, we call its observe() method with our map container element as its argument. This starts watching the element's visibility. Finally, we return a cleanup function from the useEffect() hook. This function unobserves our map container element when it's removed from the DOM or when our component is unmounted.

React.useEffect(() => {
    ...
    observer.observe(mapEle);

    return (): void => {
        observer.unobserve(mapEle);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

Loading Google Maps with a specific callback

Once the IntersectionObserver callback is triggered, we can load the Google Maps. To achieve that, we'll create a script tag that loads the Google Maps JavaScript API. The API_KEY provided by Google will be passed to the src attribute of the script. Once the API is loaded, it will call the initMap function specified within the script.

const API_KEY = '...';

const script = document.createElement('script');
script.src = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&callback=initMap`;
script.async = true;
document.body.appendChild(script);

window.initMap = initMap;
Enter fullscreen mode Exit fullscreen mode

The initMap function is responsible for creating a new instance of Google Map. It specifies the map container element and some initial options like center coordinates and zoom level.

const initMap = () => {
    const mapEle = mapRef.current;
    if (mapEle) {
        new window.google.maps.Map(mapEle, {
            center: new window.google.maps.LatLng(40.73061, -73.935242), // New York city
            zoom: 10,
        });
    }
};
Enter fullscreen mode Exit fullscreen mode

Take a look at the demo below. You can scroll down to see how the map loads dynamically:

If you'd rather not rely on the callback parameter, there's another way to ensure that the Google Map API is fully loaded before we try to use it. We can listen to its load event. This is crucial because attempting to create a new instance of the Google Map before the API is fully loaded will result in an error.

Here's how to do it:

const script = document.createElement('script');
script.addEventListener('load', () => {
    new window.google.maps.Map(mapEle, {
        center: new window.google.maps.LatLng(40.73061, -73.935242), // New York city
        zoom: 10,
    });
});
Enter fullscreen mode Exit fullscreen mode

Take a look at the demo below:

Conclusion

Using IntersectionObserver to lazy load our Google Map can greatly improve our website's performance. The map will only load when the user scrolls it into view, reducing the initial load time of the page.

This technique isn't just limited to Google Maps. It can be applied to any resource that's expensive to load. Lazy loading is a powerful tool that can optimize your site's performance and provide a better user experience.


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

Top comments (0)