Implement Google Maps into your React Project

Cody Dupuis
8 min readDec 6, 2020

Today I’m going to be going over a short guide on how to drop a Google Map component into your React project. The official NPM documentation for the library I’m using has all the instructions here if you would like to check that out instead.

Setup

First things first, go here to sign up for your API key. If you’ve done this kind of thing before, its fairly straight forward. Otherwise, you’ll need to sign up and add a new set of credentials. It’ll ask for the project name and whatnot. The url you’ll want to use while in development is localhost:3000 (React’s default).

IMPORTANT NOTE: Google requires you to add a credit card in order to get a key. The first 1000 requests are free, but after that they will begin to bill you. This key is what ties the project to you and your payment info. It is imperative that you never share this key with anyone unless they are a part of the project you’re working on or you’re just a nice person who lets random developers get free Google API services.

Once that’s all set up, run npm install dotenv —-save. At the very top level of your project, create a file called .env. Before you do anything else, go into your .gitignore file and add .env to it. This .env file is where you are going to keep your key, and you don’t want to push that info to your public GitHub repo. Even if that repo is private, it’s good practice to get comfortable using environment variables for sensitive information.

In your newly created and safely stored .env file, copy and paste your API key. It’s important to note that React only reads environment variables that are prefixed with REACT_APP, so when you store your key, it should look like this:

REACT_APP_GMAPS_KEY=<API key here, no spaces, no quotes/>

Nice! Now we need an actual Map.

The Map

Google Maps API has an extensive list of different API services. The specific one we will use to drop in your map is the Maps JavaScript API. You can read the documentation on that API here. It’s important that you also activate this API for use. Go to the Google Dev console where your credentials for this project are stored and enable the Maps JavaScript API. This API is what is going to present your Map component with the info it needs.

IMPORTANT NOTE: Once the Map is actually on the page, every page refresh will send another request. It probably won’t make a huge difference, but it’s important to remember since React re-renders components based on code changes or state. You’re going to be using a pretty solid amount of requests in development, but I’d recommend trying a method that limits re-renders unless absolutely necessary if you’re trying to stay frugal.

Once you have that API enabled, you need to install the npm library:

npm install google-maps-react --save

Let’s create a <MapContainer /> component. This will be a class component, as holding local state will be helpful. Don’t forget to import and render this component in your App.js. You’re going to need latitude and longitude coordinates, which are pretty easy to get. Drop an address into Google Maps and then right click, you should see the coordinates for whatever point you selected it. Clicking on the coordinates will automatically copy them to your clipboard.

import React, { Component } from 'react';
import { Map, GoogleApiWrapper, Marker, InfoWindow } from 'google- maps-react';
require('dotenv').config();
export default class MapContainer extends Component {
state = {
destinations: [],
mapCenter: { lat: 30.146626, lng: -92.035548 }
}
render() {
const mapStyles = {
height: "70%",
width: "50%",
position: "static"
}
return(
<div className="map-container">
<Map
google={this.props.google}
initialCenter={this.state.mapCenter}
style={mapStyles}
zoom={14}
className={"map"}>
</Map>
</div>
)
}
}
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GMAPS_KEY
})(MapContainer);

That’s a lot of info. Let’s break it down:

We have our import statements. I didn’t include <Marker /> or <InfoWindow /> just yet, but import them anyway cause I can cover that later on. We imported Map and GoogleApiWrapper. If you’re familiar with Redux and using the connect() function, then GoogleApiWrapper should be pretty straightforward. You’re exporting your component alongside an object that has a key that points to your API key, so that Google can verify who they’re giving Maps info to.

As I said before, we will need state. I started with a mapCenter key, that points to the latitude and longitude that I used in my own personal project, so feel free to substitute it with any coordinates of your choice. This value will function as our map’s center and initial marker. We then have a destinations key that points to an empty array. We will fill that array later on to render markers.

The <Map /> component itself takes in some props:

  • google is a massive object that gets passed to the map in order to do its rendering. It’s too much to cover, but feel free to check it out in your browser console.
  • intialCenter quite literally tells our map where to center itself. Passing the state object the way its formatted above will do the trick
  • zoom tells how far we want the map to be zoomed
  • style gives the Map some initial style properties. I like to create a separate object, but you can pass it as a raw object in the props if you choose to do so
  • className is my personal preference. The different tutorials I’ve seen render a map that covers the whole screen, but in my instance I wanted a smaller map centered to the screen. This can be done by giving class names to the Map and its parent <div> and using CSS:
.map-container, .map {
display: flex;
justify-content: center;
}

At this point, I would definitely run npm start and check to make sure everything renders. If it does, move on to the next section. Otherwise, check your console for errors!

Marker and InfoWindow

And important thing to note about both of these components is that they are both child components of <Map />. So the first step I want to take is adding some other state values we are going to need. Ultimately, the component state should look like this to start:

state = {
destinations: [
{location: { lat: 30.150839490122195, lng: -92.09357565868899},
name: "Address 1"},
{location: { lat: 30.10956547664899, lng: -92.05924338426283},
name: "Address 2"}
],
mapCenter: { lat: 30.146626, lng: -92.035548 },
showInfoWindow: false,
activeMarker: {},
selectedPlace: {}
}

Alright. Let’s drop our Center Marker:

<Map>
<Marker
position={this.state.mapCenter}
name={"Center Marker"}
icon={{
url: './center-marker.png',
scaledSize: new this.props.google.maps.Size(37, 37)
}}
/>
</Map>

Now we get to talk about the Marker’s props:

  • position functions the same as the Map’s initialCenter, so we can reuse the state property here
  • name is the name. That’s it.
  • icon allows you to specify your own custom icon. It accepts an object with a url key and a scaledSize key. The cool thing about this component is that if you have an image in your public directory (which is where you should be storing project images to be rendered) the above url path will work just fine. The scaledSize prop uses that complicated google prop mentioned earlier to allow you to resize your custom icon based on the Map’s size. The two numbers specify height and width, and will greatly vary on the size of your Map. You’ll probably need to play with this to get it just right.

Now that we have our center marker, let’s make a function to drop in the rest:

renderDestinations = () => {
if (this.state.destinations.length > 0) {
return this.state.destinations.map(destination => {
return <Marker
position={destination.location}
name={destination.name}
icon={{
url: './marker.png',
scaledSize: this.props.google.maps.Size(37, 37)
}}
/>
})
}
}

Now, under where you dropped your center marker, invoke this function:

<Marker />
{this.renderDestinations()}

You should be seeing your Markers now! That’s awesome! But what if we want more info on these markers? Well we are going to need another function to listen for Marker clicks.

onMarkerClick = (props, marker, event) => {
this.setState({
selectedPlace: props,
activeMarker: marker,
showInfoWindow: true
})
}

You’re going to need to pass this callback function as an onClick listener to all of your Markers. If you have the React DevTools extension, you can observe the state of your component being updated when you click on a Marker. Otherwise, drop a console.log(props.name) and when you click the Marker, your console should log the name of the Marker you clicked. This function is what we will use to render our InfoWindow.

It’s important for me to note that a Map can only have one InfoWindow as a child. Equally as important, the InfoWindow needs to be placed below the Marker. When the InfoWindow drops to the DOM, the Markers are already there for the InfoWindow to attach to. Let’s try it out:

<Map>
<Marker />
{this.renderDestinations()}
<InfoWindow
visible={this.state.showInfoWindow}
marker={this.props.activeMarker}
onClose={this.onClose}>
<div className="marker-info">
<h4>{this.state.selectedPlace.name}</h4>
</div>
</InfoWindow>
</Map>

Notice we have a <div> that’s rendering as a child to the InfoWindow. This is mainly for styling purposes.

  • visible defaults to false, and the idea is that we have a way to toggle it to true when we click a Marker
  • marker is the marker that the InfoWindow will attach itself to. This is set by the onMarkerClick() function from earlier.
  • onClose() is an event listener that will detach the window from the Marker when we close the window. We can go straight into building that now:
onClose = props => {
if (this.state.showInfoWindow) {
this.setState({
showInfoWindow: false,
activeMarker: {},
selectedPlace: {}
})
}
}

And that’s it! Now, you should be able to click on all 3 of the Markers you have on the Map. Upon clicking them, a small window with the name should pop up. Close the window and click the next one to make sure it’s all good to go!

The Final Product

import React, { Component } from 'react';
import { Map, GoogleApiWrapper, Marker, InfoWindow } from 'google- maps-react';
require('dotenv').config();
state = {
destinations: [
{location: { lat: 30.150839490122195, lng: -92.09357565868899},
name: "Address 1"},
{location: { lat: 30.10956547664899, lng: -92.05924338426283},
name: "Address 2"}
],
mapCenter: { lat: 30.146626, lng: -92.035548 },
showInfoWindow: false,
activeMarker: {},
selectedPlace: {}
}
renderDestinations = () => {
if (this.state.destinations.length > 0) {
return this.state.destinations.map(destination => {
return <Marker
position={destination.location}
onClick={this.onMarkerClick}
name={destination.name}
icon={{
url: './marker.png',
scaledSize: this.props.google.maps.Size(37, 37)
}}
/>
})
}
}
onMarkerClick = (props, marker, event) => {
this.setState({
selectedPlace: props,
activeMarker: marker,
showInfoWindow: true
})
}
onClose = props => {
if (this.state.showInfoWindow) {
this.setState({
showInfoWindow: false,
activeMarker: {},
selectedPlace: {}
})
}
}
render() {
const mapStyles = {
height: "70%",
width: "50%",
position: "static"
}
return(
<div className="map-container">
<Map
google={this.props.google}
initialCenter={this.state.mapCenter}
style={mapStyles}
zoom={14}
className={"map"}>
<Marker
position={this.state.mapCenter}
name={"Center Marker"}
onClick={this.onMarkerClick}
icon={{
url: './center-marker.png',
scaledSize: new this.props.google.maps.Size(37, 37)
}}
/>
{this.renderDestinations()}
<InfoWindow
visible={this.state.showInfoWindow}
marker={this.props.activeMarker}
onClose={this.onClose}>
<div className="marker-info">
<h4>{this.state.selectedPlace.name}</h4>
</div>
</InfoWindow>
</Map>
</div>
)
}
}
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GMAPS_KEY
})(MapContainer);

Thanks for reading! I hope this was helpful. Please read the documentation if you want to go over a couple of other components that I didn’t cover like Polygon and Polyline. Happy coding!

--

--