How to Use Mapbox in GatsbyJS: Beginner Friendly
I searched far and wide over the internet to find the perfect way to combine Mapbox and GatsbyJS. After a ton of research and trial and error, this is what I came up with.
First, download the “normal” mapbox package from npm from your terminal. I opted to use this one and not the React version because of compatibility issues. I have tested it twice using the default Gatsby starter and the Absurd starter and both showed only a grey block. I believe the reason for this was because of the version of React that these starters use. According to the react-map-gl documentation, it requires react >= 16.3, while my two starters both use react 16.14.
npm i mapbox-gl
Now for our code. I created a file called “AMap.js” because the company’s name starts with an A, but feel free to call this whatever you’d like as long as it’s not called “Map” as Mapbox comes with a Map component. I create another file called “mapstyles.css” for the marker styles and any other styles I want to include that are not styled components or inline styles. Be sure to remember the path of the CSS file if it is not included in the same folder as your map. If you don’t end up using any CSS, it can be omitted. At the top of the page, include the following:
import React from ‘react’;import mapboxgl from ‘mapbox-gl’;import ‘./mapstyles.css’
If you do not already have a Mapbox account, create one and copy your access token. Either install dotenv and include your token there (this is the most secure option), or include the following:
mapboxgl.accessToken = ‘your token here’
Now it’s time for our component. Let’s start with initializing our component and adding the constructor, which will set the initial state.
Underneath the access token in AMap.js, include the following:
class AMap extends React.Component { constructor(props) { super(props); this.state = { lng: -73.824200, lat: 45.429166, zoom: 17 }; }
}
To find the longitude and latitude, go to Google maps and right click the spot you want. Click “what’s here?” and at the bottom of the map you will see an address and coordinates. Click the coordinates and you will see the location show up on the left. Copy and paste them into lng and lat (this can be named whatever you want, I prefer to keep it short. The center property is exactly what you would expect; the center of the map that you’d like to render. If you are showing a large section, be sure to make the zoom property a low number, but this will take a bit of trial and error to create the perfect setting for your circumstance. In my case, I am showing the location of a restaurant, so my zoom is quite high.
The next step is to create the componentDidMount() method. This lifecycle method is part of the mounting process of a react component when “a component is being created and inserted into the DOM”.
Under your constructor method and above the final curly brace, insert the following:
componentDidMount() { const map = new mapboxgl.Map({ container: this.mapContainer, style: ‘mapbox://styles/mapbox/streets-v11’, center: [this.state.lng, this.state.lat], zoom: this.state.zoom });
}
This information is the most basic and necessary when creating a map. The style can be changed out with another Mapbox style (or your own!), but is needed or your map will not show up. The center and zoom properties are taken from the constructor method, and the container will be created in our JSX.
The final step in our JS file: rendering and exporting the map. We will use the most lifecycle method, render(). Here is the part where we create our map container and refer to the new mapboxgl Map component in the componentDidMount() method.
Under the componentDidMount() method and before the final curly brace. include the following:
render() { return ( <div> <div ref={el => this.mapContainer = el} className=’mapContainer’ /> </div> )}
And, finally, underneath the final curly brace, include the following:
export default AMap
The map can now be called on as a component in your main pages simply by including <AMap /> and importing it at the top of the page.
Adding a Marker
All of the information you need to create a marker is going to placed inthe componentDidMount() method. I followed the official Mapbox tutorial and edited it to work in a react/GatsbyJS environment.
The first step is to create an object called FeatureCollection that will hold all of the data to create as many markers as you need. I only need one, but I will show you how to add more than one.
Inside the componentDidMount() method, add the following:
const geojson = { type: ‘FeatureCollection’, features: [{ type: ‘Feature’, geometry: { type: ‘Point’, coordinates: [this.state.lng, this.state.lat] }, properties: { title: ‘Marker 1’, description: ‘270 Bord-du-Lac, Pointe-Claire’ } },
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-122.414, 37.776]
},
properties: {
title: 'Marker 2',
description: 'San Francisco, California'
} }]};
This will create two points, one in Pointe-Claire, Quebec, and another in San Francisco, California (taken from the tutorial mentioned above).
Next, we need to loop over all of the markers (which are referred to as “features” in our array) which is simply done with forEach. Under the geojson element while still inside the componentDidMount() method, add the following:
geojson.features.forEach(function(marker) { // create a HTML element for each feature const el = document.createElement(‘div’); el.className = ‘marker’;
// make a marker for each feature and add to the map new mapboxgl.Marker(el) .setLngLat(marker.geometry.coordinates) .addTo(map);});
As long as you are familiar with basic JavaScript, this should be pretty self explanatory. Inside the geojson object, access the features array. For each element inside the array, create a new div with the class “marker”. Then, create a new Marker element on the map and set the coordinates to whatever is inside the geometry property. In our case, we have coordinates which are set inside our constructor, and another set of coordinates that are manually added to the geojson object inside geometry.
The final step is to add styling to our point. I used Mapbox marker found here and saved it in the same folder as the rest of my map files. You can change the styling to the way you see fit. In mapstyles.css, add the following:
.marker { background-image: url(‘mapbox-icon.png’); background-size: cover; width: 50px; height: 50px; border-radius: 50%; cursor: pointer;}
Bugs I ran into, and their solutions
- If your map does not show up at all at this point, go to mapstyles.css and include the following:
.mapboxgl-map {width: 100% !important;}
The !important overrides whatever properties are already present in the class or ID you are calling on.
I am sure there is another way to do this, so please let me know in the comments if you have a different solution.
2. The tiles of the map were like an unsolved puzzle. This was an easy fix, and in the console it actually points you in the right direction:
react_devtools_backend.js:2430 This page appears to be missing CSS declarations for Mapbox GL JS, which may cause the map to display incorrectly. Please ensure your page includes mapbox-gl.css, as described in https://www.mapbox.com/mapbox-gl-js/api/.
If you are using Helmet (which I highly recommend you do), include the following:
<link href=’https://api.tiles.mapbox.com/mapbox-gl-js/v1.3.1/mapbox-gl.css' rel=’stylesheet’ />
If you have the same error but the map does not show an unsolved puzzle, include the following to solve it:
<link href=’https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css' rel=’stylesheet’ />
And that’s it! Simple as that. Let me know what problems you ran into on the way or if you have found a better way to implement Mapbox into a GatsbyJS project.