Use Mapbox GL JS in a React app
This tutorial will walk you through adding a Mapbox GL JS map to a React project. You will learn how to write a custom React component to add an interactive map to a div. Once the map is created, you will learn how to respond to map interactions to update other parts of your app, and control the map based on events that happen outside of the map.
Mapbox GL JS manipulates the DOM directly and does not export React components and hooks. This tutorial demonstrates direct use of the Mapbox GL JS API within a custom React component, which is our recommended implementation method.
For some use cases, a third-party wrapping library like react-map-gl
may be useful.
You'll build a full-screen Mapbox GL JS web map along with some custom UI elements.
- A panel that displays the map's center and zoom as the user moves the map.
- A reset button that restores the original map view after the user has interacted with it.
You can try out the finished product in this browser preview panel:
Getting started
You will need a few things in place before starting this tutorial:
- A Mapbox access token. Your Mapbox access tokens are on your Account page.
- A text editor. Use a text editor like Visual Studio Code to edit your code.
- Node.js and npm. Follow the Downloading and installing Node.js and npm guide for instructions.
- Familiarity with a terminal to execute node.js and npm commands.
- 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.
Step 1: Create a React App with Vite
First, scaffold a React app using the popular build tool Vite.
- Run
npm create vite@latest
from your terminal and answer a few prompts. Give your project a name likemy-map-app
, chooseReact
from the list of frameworks, then chooseJavascript
from the list of variants.
You will see a helpful message as your new app is created.
% npm create vite@latest
✔ Project name: … my-map-app
✔ Select a framework: › React
✔ Select a variant: › JavaScript
Scaffolding project in /Users/mapguru/my-map-app...
Done. Now run:
cd my-map-app
npm install
npm run dev
Follow the instructions to change directories, install dependencies (npm install
) run the development server (npm run dev
).
% npm run dev
> my-map-app@0.0.0 dev
> vite
VITE v5.4.4 ready in 111 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
You will see some a message in your terminal saying the development server is available at http://localhost:5173/
. Open this URL in your browser.
Step 2: Create a map container element
Now that you have a React app scaffolded, let's update App.js
to prepare for a map. Mapbox GL JS works by adding a map to an existing element, usually a div. This is referred to as the map container, and should be styled with CSS to position and size the map within the app.
In your code editor, replace the existing code in src/App.jsx
with the snippet below. This adds an empty div
with an id
of map-container
.
import './App.css'
function App() {
return (
<>
<div id='map-container' />
</>
)
}
export default App
Next, add CSS to make the root
div full screen, and the map-container
full height and width. (Some additional CSS rules are included to style elements that are added in later steps.) Since the map container div has no content, it will be invisible on the page. You can temporarily add a background-color
rule to help see where it is positioned.
Replace the contents of src/App.css
with the following snippet:
A common pitfall when working with Mapbox GL JS is adding a map to a container that has no height. Make sure your map container is styled properly before creating a new map.
#root {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
#map-container {
height: 100%;
width: 100%;
/* temporarily set the background color so we can tell where the map container is positioned */
background-color: lightgrey;
}
.sidebar {
background-color: rgb(35 55 75 / 90%);
color: #fff;
padding: 6px 12px;
font-family: monospace;
z-index: 1;
position: absolute;
top: 0;
left: 0;
margin: 12px;
border-radius: 4px;
}
.reset-button {
position: absolute;
top: 50px;
z-index: 1;
left: 12px;
padding: 4px 10px;
border-radius: 10px;
cursor: pointer;
}
Finally, delete the contents of src/index.css
to its styles do not interfere with the map container's positioning.
/* index.css should be empty */
Save your changes across all files. The development server will hot reload with each saved change. If you see a light gray div filling the viewport, you have successfully positioned your map container for a full screen map.
With your map container created and rendering in the app, you are ready to instantiate a Mapbox GL JS map.
Step 3: Add a Mapbox GL JS Map
First, install the mapbox-gl
package using npm
. See the installation guide for more information about installation options.
$ npm install mapbox-gl
At the top of App.jsx
, import useRef
and useEffect
, from react
, we will need these when setting up the map. You must also import mapbox-gl
along with its associated CSS.
import { useRef, useEffect } from 'react'
import mapboxgl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css';
import './App.css'
...
Next, add two refs using the useRef
hook. The first ref will persist the map instance so you can control the map throughout the lifecycle of this component. The second ref exposes the map container's HTML element, and is used to tell Mapbox GL JS where to create the map.
Be sure to add the ref
prop to your map container div
.
function App() {
const mapRef = useRef()
const mapContainerRef = useRef()
...
return (
<>
<div id='map-container' ref={mapContainerRef}/>
</>
)
}
With the refs created, you are ready to instantiate the map. Follow these steps:
- Copy the code below and paste it between the refs, but above the return of the div.
- This code adds a
useEffect
hook toApp.jsx
with an empty dependency array. This will run once when the component is first mounted and then callsnew mapboxgl.Map()
to add the map.
-
Make sure to assign
mapboxgl.accessToken
to an access token from your account page. The access token is used for billing, and associates this map with your Mapbox account. -
Pass an options object into
new mapboxgl.Map()
to control the map. In this example, only thecontainer
option is specified asmapContainerRef.current
. Mapbox GL JS will create the new map in your map containerdiv
.
new mapboxgl.Map()
returns new instance of the Map
class, and is assigned to mapRef.current
so we can use it later.
function App() {
...
useEffect(() => {
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
});
return () => {
mapRef.current.remove()
}
}, [])
...
}
After these steps, your App.jsx
should look like this:
import { useRef, useEffect } from 'react'
import mapboxgl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css';
import './App.css'
function App() {
const mapRef = useRef()
const mapContainerRef = useRef()
useEffect(() => {
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
});
return () => {
mapRef.current.remove()
}
}, [])
return (
<>
<div id='map-container' ref={mapContainerRef}/>
</>
)
}
export default App
Save your changes, make sure your development server is running and check the browser.
You will see a globe on a background of space and stars, the Mapbox logo, and an attribution notice. Hello, World!
You can use your scroll wheel to zoom in and out, and drag the globe to rotate it.
You probably don't want to start your map zoomed all the way out like this, so you can add center
and zoom
options when calling new mapboxgl.Map()
.
Specify center
as longitude and latitude coordinates in an array ([longitude, latitude]
). zoom
ranges from 0
, zoomed all the way out to see the whole globe, to 22
, zoomed in to street level.
For help finding coordinates for locations and appropriate zoom levels for the view you want, use our handy Location Helper tool.
...
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
center: [-74.0242, 40.6941],
zoom: 10.12
});
...
Here's what the map will look like zoomed into New York City:
With your new Mapbox GL JS map added, you are ready add code to integrate it into the rest of your app. We will cover integrations in both directions, updating app state in response to map events, and updating the map in response to events happening in the app.
Step 4: Respond to map events
There are several events generated by a Mapbox GL JS map which you can listen for to trigger functionality in your React app. In this step, we will listen for the events that occur when a user pans and zooms the map, and use them to update state in our React component.
For this example we will listen for the move
event, which fires continuously with each change to the map's view, such as when the user drags or zooms the map. We can then query the map for its current center point coordinates and zoom level, and display these values in the app's UI.
Update App.jsx
with the code highlighted below.
First, move the starting center point coordinates and zoom level to constants so they can be used for both initializing the map and as the initial state values.
Add two useState()
hooks to store center
, and zoom
, using the constants defined above as their default values.
Update mapbox.Map()
to use the center
, and zoom
state variables.
Next, add an event listener for the map's move
event in the useEffect()
hook. When move
is fired, the callback for this listener queries the map using getCenter()
and getZoom()
and updates both state variables.
Finally, add a display div before the map container, adding placeholders lng
, lat
, and zoom
. (This div is already styled using the CSS added in step 2). Use toFixed()
to limit the number of decimal places for a cleaner display.
import { useRef, useEffect, useState } from 'react'
import mapboxgl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css';
import './App.css'
const INITIAL_CENTER = [
-74.0242,
40.6941
]
const INITIAL_ZOOM = 10.12
function App() {
const mapRef = useRef()
const mapContainerRef = useRef()
const [center, setCenter] = useState(INITIAL_CENTER)
const [zoom, setZoom] = useState(INITIAL_ZOOM)
useEffect(() => {
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
center: center,
zoom: zoom
});
mapRef.current.on('move', () => {
// get the current center coordinates and zoom level from the map
const mapCenter = mapRef.current.getCenter()
const mapZoom = mapRef.current.getZoom()
// update state
setCenter([ mapCenter.lng, mapCenter.lat ])
setZoom(mapZoom)
})
return () => {
mapRef.current.remove()
}
}, [])
return (
<>
<div className="sidebar">
Longitude: {center[0].toFixed(4)} | Latitude: {center[1].toFixed(4)} | Zoom: {zoom.toFixed(2)}
</div>
<div id='map-container' ref={mapContainerRef} />
</>
)
}
export default App
As you drag and zoom the map, your new event listener fires, updating the state and triggering a component render. Try it out here:
Now that you know how to respond to map events, you can move on to controlling the map using external events.
Step 5: Control the Map from external events
In this step, you'll add a button to your app's UI and trigger a response from the map when it is clicked. More specifically, you'll add a Reset button which will restore the map's view to its original location after the user moves and pans the map.
Add a <button>
before the map container div and set its onClick
prop to handleButtonClick
. (This button is already styled using the CSS added in step 2)
Next, create a new function handleButtonClick
and call the map's flyTo()
method, specifying the center
and zoom
to animate the map's camera to using the constants defined in step 4.
Save your work and check your browser. Move the map by dragging and zooming, then click the Reset button. The map will smoothly transition back to the original center point and zoom.
Notice that the move
event is fired continuously as flyTo()
animates the map, so there is no need for additional code to update the center
, and zoom
UI after calling flyTo()
You can expand the hidden sections in this code snippet to see the final code for App.jsx
.
Move the map and click the Reset button. handleButtonClick
fires and restores the original camera position.
Congratulations on completing this Mapbox tutorial. You've learned the basics of setting up a Mapbox GL JS map in a React app:
- Instantiating a Mapbox GL JS Map in a
useEffect
hook - Listening for map events and updating app state
- Listening for events in the app and controlling the map
Next Steps
Here are some ideas for improving your new component that you can try on your own:
- Only enable/show the Reset button when the user has already dragged the map or changed the zoom level.
- Specify a different map style.
- Slow down the
flyTo
duration. - Specify additional options when creating the map.
- Add more buttons that fly the map to locations of interest.
Additional Resources
Be sure to browse the Mapbox GL JS examples, most of which include React code snippets to show various techniques and best practices.
- Add a default marker to a web map
- Add a polygon to a map using a GeoJSON source
- Center the map on a clicked feature
You can also check our full-featured demo applications with source available code built with React:
- Real estate app demo source code