Use Mapbox GL JS in a React app
Familiarity with React and front-end development concepts.
React is a popular JavaScript library used for building user interfaces. Since React manipulates the DOM, it can be confusing to connect React with other libraries that also manipulate the DOM and manage state — like Mapbox GL JS.
In this tutorial, you will learn how to create a React web app that uses Mapbox GL JS to render a map, display the coordinates of the map center point and its zoom level, then update the display when a user interacts with the map. You will be able to use the principles discussed in this tutorial to create more complex apps that use both React and Mapbox GL JS. This tutorial shows code for React Hooks and Class Components.
Getting started
- A Mapbox access token. Your Mapbox access tokens are on your Account page.
- Mapbox GL JS. Mapbox GL JS is a JavaScript library used for building web maps.
- A text editor. Use the text editor of your choice for writing HTML, CSS, and JavaScript.
- Node.js and npm. To run the commands necessary to run your React app locally, install Node.js and npm.
- Working familiarity with React. You don't need to have a lot of experience using React to complete this tutorial, but you should be familiar with the underlying concepts and workflows.
Set up the React app structure
To get started, create a new folder called use-mapbox-gl-js-with-react
.
In the use-mapbox-gl-js-with-react
folder, create a new file:
package.json
: This file is used to specify all of the Node packages that your app requires, including React and Mapbox GL JS.
In the use-mapbox-gl-js-with-react
folder, create a folder called public
. In the public
folder, create one file:
index.html
: This HTML file will display the rendered Mapbox map that your users will be able to interact with.
In the use-mapbox-gl-js-with-react
folder, create another folder called src
. In the src
folder, create three files:
App.js
: This JavaScript file will set up the React app.index.css
: This CSS file will contain styles to format the map and sidebar correctly.index.js
: This JavaScript file will render the Mapbox map to the browser.
Once you have created these folders and files, you will have the following file structure:
Copy the following code into package.json
:
{"name": "use-mapbox-gl-js-with-react","version": "0.1.0","private": true,"dependencies": {"mapbox-gl": "^2.14.1","react": "^17.0.2","react-dom": "^17.0.2","react-scripts": "^4.0.3"},"scripts": {"start": "react-scripts start","build": "react-scripts build"},"eslintConfig": {"extends": ["react-app"]},"browserslist": ["defaults","not ie 11"],"author": "Mapbox","license": "MIT"}
In addition to the React-specific Node packages react
and react-dom
, this file also requires react-scripts
which is also known as Create React App to build the app, and mapbox-gl
to access Mapbox GL JS.
Save your changes.
In the command line, navigate into the use-mapbox-gl-js-with-react
folder you created. In that folder, run the command npm install
, which will install all the Node packages that you specified in the package.json
file. This step also creates the package-lock.json
file.
Create the HTML page
Open the public/index.html
file and paste the following code into it:
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><metaname="description"content="Create a React web app that uses Mapbox GL JS to render a map"/><title>Use Mapbox GL JS in a React app</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
This code creates the structure of the HTML page your users will see. There is a <div>
element with the ID root
in the <body>
of the page. This <div>
is the container in which the React app will be rendered on the page.
Save your changes.
Create the React app
Open the src/index.js
file. Add the following to import two stylesheets and the Mapbox GL JS map that you will build:
import React from 'react';import ReactDOM from 'react-dom';import 'mapbox-gl/dist/mapbox-gl.css';import './index.css';import App from './App'; ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root'));
The first stylesheet contains the Mapbox GL JS styles to display the map. The second stylesheet is the index.css
file that you created earlier, which is where you will add your app-specific CSS.
Add Mapbox GL JS
Open the src/App.js
file. Add the following import statement to the top of the file:
import React, { useRef, useEffect, useState } from 'react';
Next, import Mapbox GL and add your access token. To use Mapbox GL with Create React App, you must add an exclamation point to exclude mapbox-gl from transpilation and disable the eslint rule import/no-webpack-loader-syntax
. Set the mapboxgl
accessToken
property to the value of your Mapbox access token:
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
Now you can set up the React app! To create the structure that you will add the code from the next few steps in, add the following to the bottom of App.js
:
export default function App() {
Save your changes.
Set the app's default state
Next, you will create some defaults for your app to use for the initial latitude, longitude, and zoom of the map. Add the following inside App
:
const mapContainer = useRef(null);const map = useRef(null);const [lng, setLng] = useState(-70.9);const [lat, setLat] = useState(42.35);const [zoom, setZoom] = useState(9);
The state stores the longitude, latitude, and zoom for the map. These values will all change as your user interacts with the map.
Next, initialize the map. The following code will be invoked right after the app is inserted into the DOM tree of your HTML page.
useEffect(() => {if (map.current) return; // initialize map only oncemap.current = new mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v12',center: [lng, lat],zoom: zoom});});
The Mapbox map is initialized within a React Effect hook or the componentDidMount()
lifecycle method, if you are using classes. Initializing your map here ensures that Mapbox GL JS will not try to render a map before React creates the element that contains the map. You also set the following options inside the map initialization:
- The
container
option tells Mapbox GL JS to render the map inside a specific DOM element. Here, the app expects to receive amapContainer
useRef
orref
if you are using class components. Later in this tutorial, you will assign theref
to an HTML element that will act as the map container. - The
style
option defines the style that the map will use (mapbox://styles/mapbox/streets-v12
). - The
center
andzoom
options set the center coordinates and zoom level of the map using the values oflng
,lat
, andzoom
that are stored in state. - If you are using hooks, you also created a
map
useRef
to store the initialize the map. Theref
will prevent the map from reloading when the user interacts with the map.
Save your changes.
Render the map
Now you need to render the map in your app. The entry point to initialize a Mapbox map in a React app is through a single element provided in the return
statement. Add the following code to your app above the closing curly brace of App
:
return (<div><div ref={mapContainer} className="map-container" /></div>);
The mapContainer
ref
specifies that App
should be drawn to the HTML page in a new <div>
element.
The map needs a few styling rules to render correctly. Add the following code to the index.css
file:
.map-container {height: 400px;}
Save your changes. In the command line, run the command npm start
. This starts a local server and opens a new page in your browser that contains the new Mapbox map.
If you open your browser's developer console you may see no-unused-vars
warnings. Don't worry — you use these variables in the next step.
Store the new coordinates
Next, you need to create a function that stores the new latitude, longitude, and zoom that you get when a user interacts with the map. You will write a Mapbox GL JS map.on('move')
function that sets the state to these new values when a user moves the map. If you are using hooks, use the previous useEffect
and add the following code below the map's initialization. If you are using classes, add the following code inside componentDidMount()
:
map.current.on('move', () => {setLng(map.current.getCenter().lng.toFixed(4));setLat(map.current.getCenter().lat.toFixed(4));setZoom(map.current.getZoom().toFixed(2));});
This function uses useState()
if you are using hooks, or setState()
if you are using classes, to reset the values of lng
, lat
, and zoom
when the map moves. It also uses the following methods:
getCenter()
, a Mapbox GL JS method, to get the new longitude and latitude of the point at the center of the map.getZoom()
, a Mapbox GL JS method, to determine the zoom level that the map is set to.toFixed()
, a JavaScript method, to truncate the resulting floating point number to the specified number of digits.
Display the coordinates
Now that you are able to collect and store this information, you can use return
to display it on the map. Inside the opening tag of the <div>
you created to hold the map, add a new <div>
to display the longitude, latitude, and zoom of the map. The return
statement will look like this now:
return (<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}</div><div ref={mapContainer} className="map-container" /></div>);
The sidebar needs a few styling rules to display on the page correctly. Add the following CSS to the index.css
file:
.sidebar {background-color: rgba(35, 55, 75, 0.9);color: #fff;padding: 6px 12px;font-family: monospace;z-index: 1;position: absolute;top: 0;left: 0;margin: 12px;border-radius: 4px;}
Save your work and go back to the browser page. There is a sidebar now in the upper left corner of the map that is styled according to the CSS rules you set in index.css
. The sidebar shows the current latitude and longitude of the center of the map, as well as the zoom level. Now when you zoom and pan around the map, the contents of the sidebar will update.
Final product
You have created a React app that uses Mapbox GL JS to render a map, display the center coordinates and the zoom level of the map, and then update that display when a user interacts with the map.
The final index.html
page will look like the following:
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><metaname="description"content="Create a React web app that uses Mapbox GL JS to render a map"/><title>Use Mapbox GL JS in a React app</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
The final index.js
file will look like the following:
import React from 'react';import ReactDOM from 'react-dom';import 'mapbox-gl/dist/mapbox-gl.css';import './index.css';import App from './App'; ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root'));
The final App.js
file will look like the following:
import React, { useRef, useEffect, useState } from 'react';import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; export default function App() {const mapContainer = useRef(null);const map = useRef(null);const [lng, setLng] = useState(-70.9);const [lat, setLat] = useState(42.35);const [zoom, setZoom] = useState(9); useEffect(() => {if (map.current) return; // initialize map only oncemap.current = new mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v12',center: [lng, lat],zoom: zoom}); map.current.on('move', () => {setLng(map.current.getCenter().lng.toFixed(4));setLat(map.current.getCenter().lat.toFixed(4));setZoom(map.current.getZoom().toFixed(2));});}); return (<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}</div><div ref={mapContainer} className="map-container" /></div>);}
The final index.css
file will look like the following:
.map-container {height: 400px;} .sidebar {background-color: rgba(35, 55, 75, 0.9);color: #fff;padding: 6px 12px;font-family: monospace;z-index: 1;position: absolute;top: 0;left: 0;margin: 12px;border-radius: 4px;}
Next steps
Now that you have created a React app that uses Mapbox GL JS, you can explore other, more complex examples in the Mapbox React examples GitHub repository.