Mapbox is essentially a third-party JavaScript library that allows developers to build maps and display them in a web browser application. It's really great for users especially when maps include interactivity such as zooming in and out of a map, adding markers, and popups, and animated geographic data. Mapbox overall is super cool! However, it mostly works with Javascript. So how can we add this nifty tool to our React app?
Along comes React-Map-Gl which was first created by Uber's Visualization team where they saw how powerful Mapbox could be and sought ways to implement it using React programming. Go here for a deep dive into their Design Philosophy and Limitations. It's also open source so anyone is welcome to contribute to making it even better for future iterations. You can do so here.
When I first came across Mapbox to use in my Capstone project, I noticed that a lot of the current documentation, tutorials, and videos that existed were for React-Mapbox-Gl version 6.1 or 5.3. However, they've recently upgraded to v7.0 in Feb 2022 and changed MANY things behind the scenes so all the old tutorials out there needed some revamping and reconfiguring to get it to work with the newer version. I also noticed that there weren't a lot of new tutorials or videos incorporating v7.0 so I decided to start simple and write about how to create a map and add markers using this awesome API. For an in-depth look at the differences in versions, take a look here.
Set-Up
Now, to get started, download the package:
npm install --save react-map-gl mapbox-gl
check that your dependencies look like this:
"dependencies": {
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-map-gl": "^7.0.0",
"mapbox-gl": "^2.0.0"
}
and import Map into your component along with Marker like so:
import Map, {Marker} from 'react-map-gl';
Also, you'll need to import css styling as well so we can have the ability to set height and width to our map or to add additional styling to your map if you want to get fancy. But bear this in mind as we'll be needing this later.
import 'mapbox-gl/dist/mapbox-gl.css';
In order to use Mapbox in our app, we'll need to sign up with an account and enter some billing information. Even though it's free to use for developers, it's only free up to a certain point which is about 50,000 loads or so a month, and after that they'll charge a fee. Don't worry though because during development you should never come close to this number, but keep on eye on your dashboard as it will have a bar to let you know how many times your map has been mounted. For example, during pre and post development, I only had a total of 368 loads.
Once an account has been created, you'll want to create a token for your app. Nothing fancy needs to be added or modified to create that token, but make sure it's different from your default public token. You'll want to protect that token from the public which means we don't want to set it to a variable directly on our map component or anywhere in our app, but instead, make sure that it is set to .env.local
file in your react app. Make sure to check your .gitignore
file and that .env.local
comes with it. It usually does when you've used create-react-app
. Set REACT_APP_MAPBOX_ACCESS_TOKEN
as the variable name in this .env.local
file to your token.
REACT_APP_MAPBOX_TOKEN="your token goes here"
The reason we do this is to prevent someone from taking our token and abusing it. We don't want anyone to take our token and go beyond that 50,000 loads. Not cool. Again, never commit your token in clear text anywhere in your app into Github... For more information on how to secure your Mapbox token, here's some more documentation on it as well as on the tokens themselves.
You'll be calling this variable later so keep this in mind for now. That should be all the set-up we need for now. Let's get to coding.
Getting your map on the browser
For our purposes here, we're going to start by simply showing the map and adding markers from our backend database.
But first we want to set our initial viewport to where we want our app to load when first mounted on the browser. Let's make this controlled so we'll set state outside of the JSX like so.
function ReactMap() {
const [viewState, setViewState] = useState({
latitude: 39.8283,
longitude: -98.5795,
zoom: 3.5
});
We no longer need to use width or height in this version within state so latitude, longitude, and zoom is all we need. We'll be setting that in styles in the JSX later. Here, the latitude and longitude is the center of the USA with a zoom of 3.5. The smaller the zoom number, the more zoomed out it will be, the higher the number, the more zoomed in it will be on those coordinates.
Next, let's get into the return, call on <Map>
, and take all of our set up and write it up, like so.
return (
<Map
{...viewState}
onMove={event => setViewState(event.viewState)}
style={{width: 800, height: 600}}
mapStyle="mapbox://styles/mapbox/streets-v9"
mapboxAccessToken={REACT_APP_MAPBOX_TOKEN}
>
</Map>
);
}
As you can see, we need to create a copy of our viewState
using the spread operator to grab that state we initialized it as. Next we include the onMove
callback whenever we want to manage the camera or view state for the map. This is triggered when the user moves the map around. Next is the styling of the map, how big we want the map to be on our page when it loads. Change specifications to your liking.
Next we include the mapStyle
and add a styled map of our choosing from Mapbox. They have a gallery of already created map styles that you can use or gather inspiration from to create your own. Save one you created to your gallery, and grab the Style Url and plug it in. The one used above is a basic style. However, I actually am using one of the themed maps for my application, as you can see from the finished product.
And finally, we call on our REACT_APP_MAPBOX_TOKEN
by setting it to
mapboxAccessToken
so that your map from Mapbox is accessible.
Your final code should look like this:
import React from 'react';
import Map, {Marker} from 'react-map-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
function ReactMap() {
const [viewState, setViewState] = useState({
latitude: 39.8283,
longitude: -98.5795,
zoom: 3.5
});
return (
<Map
{...viewState}
onMove={event => setViewState(event.viewState)}
style={{width: 800, height: 600}}
mapStyle="mapbox://styles/mapbox/streets-v9"
mapboxAccessToken={REACT_APP_MAPBOX_TOKEN}
>
</Map>
);
}
export default ReactMap;
Now check your browser by running npm start
in your terminal.
Time to add some markers
Now that our app is there, it's looking a little bland. Let's add some markers to it, shall we?
In my app, I have data that I created on my backend using Ruby. However, knowing that Mapbox requires coordinates of latitude and longitude, I was able to include this in my seeds data to create the markers.
You can decide to have your Markers controlled or uncontrolled, but in our case, let's keep things consistent and have app controlled.
const [dataMarkers, setDataMarkers] = useState([])
useEffect(() => {
fetch('/markers')
.then(res => res.json())
.then(data => setDataMarkers(data))
}, []);
First, I'm initializing the state of my Markers to an empty array so that when I fetch the data from my backend, it gathers the objects into an array and then I reset the state to my incoming data.
const mapMarkers = () => {
dataMarkers.map((dataMarker) => {
return (
<Marker
key={dataMarker.id}
longitude={dataMarker.longitude}
latitude={dataMarker.latitude}
>
<img src="/glowsticks.svg" alt="Festival Icon"/>
</Marker>
)
})
}
Next, we're creating a mapMarkers
function, taking our data that came through the get request, and mapping over each marker. After mapping over the data, we create each Marker with a key, a latitude, and a longitude that we have saved on our backend. We then will invoke mapMarkers
within the JSX.
I also created my own SVG of glowsticks for my festival map. If you want to use your own SVG, you can make one for free at Adobe or anywhere online. Once you've made one, save it to your public folder in your app and plug in the relative path. Don't forget to add alt text!
Et viola! Restart your terminal and take a look at all your markers.
The final product
Our final code should look like this:
import React from 'react';
import Map, {Marker} from 'react-map-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
function ReactMap() {
const [viewState, setViewState] = useState({
latitude: 39.8283,
longitude: -98.5795,
zoom: 3.5
});
const mapMarkers = () => {
dataMarkers.map((dataMarker) => {
return (
<Marker
key={dataMarker.id}
longitude={dataMarker.longitude}
latitude={dataMarker.latitude}
>
<img src="/glowsticks.svg" alt="Festival Icon"/>
</Marker>
)
})
}
return (
<Map
{...viewState}
onMove={event => setViewState(event.viewState)}
style={{width: 800, height: 600}}
mapStyle="mapbox://styles/mapbox/streets-v9"
mapboxAccessToken={REACT_APP_MAPBOX_TOKEN}
>
</Map>
{mapMarkers()}
);
}
export default ReactMap;
And our outcome should look like this:
I may create a series of these and have another post on adding popup functionality. Let me know your thoughts in the comments!
Top comments (2)
Awesome blog, definitely bookmarking this for later.
Thanks so much for this, I am glad you specified the version for your packages. It seems the recent version has issues.