Easy google maps setup using google maps api in React
Pre-requisite
Before going through this article, it is primordial that you have a setup google maps api key. Here is a link to the associated documentation:
https://developers.google.com/maps/documentation/javascript/get-api-key
Context
For a recent project at Inato, we wanted to display a map with classic features such as pin point of interest, clusters, zoom on locations…
Our product being based on React and Typescript, we used the following tools to build a consistent solution:
- Google Clouds Platform
- @googlemaps/react-wrapper
- @googlemaps/markerclusterer
In this article, you will find a complete walkthrough to help you:
- Display a map
- Add marker locations
- Add marker clusters
Display a map
To start this project, we need to display a working map in our App.
As a pre-requisite, we need to install the following:
- @googlemaps/react-wrapper: to provide your google account’s api key and authenticate
- @types/google.maps: to get google typings
yarn add @googlemaps/react-wrapper
yarn add -D @types/google.maps
First things first, we want to build a Wrapper
that will allow us to authenticate to google maps and hence, load our map successfully.
import React from "react";
import { Wrapper } from "@googlemaps/react-wrapper";
export const GoogleMapsWrapper = ({
children,
}: {
children: React.ReactNode;
}) => {
// Ideally we want the apiKey to be fetch from an environment variable
const apiKey = "yourApiKey";
if (!apiKey) {
return <div>Cannot display the map: google maps api key missing</div>;
}
return <Wrapper apiKey={apiKey}>{children}</Wrapper>;
};
Let’s now use, in the component that will need to display the map, our new GoogleMapsWrapper
by passing the Map component as its child.
Nb: Another possible solution, is to put the GoogleMapsWrapper
at the App level. I don’t recommend it though, because you might not want to have your whole app not rendering if you dont have a valid api key.
export const MapComponent = () => (
<GoogleMapsWrapper>
<div>Insert Map component here<div/>
</GoogleMapsWrapper>
);
Everything is now set to welcome our GoogleMaps
component, let’s build it!
import React, { useEffect, useRef } from "react";
const DEFAULT_CENTER = { lat: 48.8566, lng: 2.3522 };
const DEFAULT_ZOOM = 7;
export const GoogleMaps = () => {
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
// Display the map
if (ref.current) {
new window.google.maps.Map(ref.current, {
center: DEFAULT_CENTER,
zoom: DEFAULT_ZOOM,
});
}
}, [ref]);
return (
<div
ref={ref}
style={{ width: "1000px", height: "700px" }}
/>
);
};
Nb: Do not forget to pass a width
and a height
to the map or it won’t be displayed !
Finally, use the GoogleMaps
component in the MapComponent
:
export const MapComponent = () => (
<GoogleMapsWrapper>
<GoogleMaps />
</GoogleMapsWrapper>
);
Congratulations, we now have a map rendered in the component 🎉
Add marker locations
Now that we have a map displayed, we would like to display some markers on it. For the sake of the example, let’s suppose that we have some locations defined by their longitude and latitude.
To start, we can write a function addSingleMarkers
. It will be used to create, for each given location, an instance of google.maps.Marker
. These markers will be passed to our map to load the locations.
export const addSingleMarkers = ({
locations,
map,
}: {
locations: ReadonlyArray<google.maps.LatLngLiteral>;
map: google.maps.Map | null | undefined;
}) =>
locations.map(
position =>
new google.maps.Marker({
position,
map,
}),
);
Nb: google.maps.LatLngLiteral
is just an object {lat: number; lng: number}
The next step is to display the markers when rendering the map. To do it, we can do two things:
- add a new prop
locations
in the component, passed by the parent - call the freshly created method
addSingleMarkers
in theuseEffect
and pass it the locations. It will run through each locations and display markers on the map on render.
import React, { useCallback, useEffect, useRef } from "react";
import { addSingleMarkers } from "./markers";
const DEFAULT_CENTER = { lat: 48.8566, lng: 2.3522 };
const DEFAULT_ZOOM = 7;
export const GoogleMaps = ({
locations,
className,
}: {
locations: ReadonlyArray<google.maps.LatLngLiteral>;
className?: string;
}) => {
...
useEffect(() => {
// Display the map
if (ref.current) {
const map = new window.google.maps.Map(ref.current, {
center: DEFAULT_CENTER,
zoom: DEFAULT_ZOOM,
});
// Displays single markers on map when called
addSingleMarkers({ locations, map });
}
}, [ref, locations]);
...
};
Of course, let’s not forget to update the MapComponent
to provide the locations.
// I took the example of adding two markers for these two locations
export const LOCATIONS = [
{ lat: 48.8566, lng: 2.3522 },
{ lat: 47.1533, lng: 2.9123 },
];
export const MapComponent = () => (
<GoogleMapsWrapper>
<GoogleMaps locations={LOCATIONS} />
</GoogleMapsWrapper>
);
We are now able to see on the map the two markers that correspond to the locations passed to the component !
Add marker clusters
Let’s now add the marker clusters behavior. We want to specify a range in which all markers would be grouped into a single marker cluster.
First, we need to add another library to handle clusters: @googlemaps/markerclusterer
yarn add @googlemaps/markerclusterer
Just like we did for single markers, we can build a method addClusterMarkers
that will be used to compute marker clusters and display them on the map. We need to use the method addSingleMarkers
that we created just before.
import {
MarkerClusterer,
SuperClusterAlgorithm,
} from '@googlemaps/markerclusterer';
import { addSingleMarkers } from './addSingleMarkers';
export const addClusterMarkers = ({
locations,
map,
}: {
locations: ReadonlyArray<google.maps.LatLngLiteral>;
map: google.maps.Map | null | undefined;
}) => {
const markers = addSingleMarkers({locations, map});
// Merge markers into clusters
new MarkerClusterer({
markers,
map,
algorithm: new SuperClusterAlgorithm({
radius: 350, // cluster size
}),
});
};
Nb: the radius
attribute of SuperClusterAlgorithm
defines the radius of the cluster. Increasing its value will group more markers under the same cluster (and hence create less clusters). Decreasing does the opposite.
Same as we did earlier, the next step is to pass our clusters to the map, to load them during when rendering the map. Here, we can simply update the useEffect
in GoogleMaps
component to load clusters instead of single markers.
// GoogleMaps.tsx
useEffect(() => {
// Display the map
if (ref.current) {
const map = new window.google.maps.Map(ref.current, {
center: DEFAULT_CENTER,
zoom: DEFAULT_ZOOM,
});
// Displays cluster markers on map when called
addClusterMarkers({ locations, map });
}
}, [ref, locations]);
And that’s basically it, markers are now grouped under clusters when they are within the radius defined above !
In case you need it, here is a full Github repository that does the whole implementation and allow you to run all of the above locally: https://github.com/scottpicquerey/react-google-maps-api
Thank you very much for reading this article 🙏
For more information you can check the Github pages of the packages installed above and Google Maps Platform’s documentation.
Special thanks to Vincent Francois for helping me on the article 🎉