Add beautiful maps to your React app with MapBox GL

MapBox makes it so easy to add fully interactive maps to your React app, here is how you can get started.

Brian Ruiz

· 954 views

I recently had to add a map to a React app I was working on. I had't worked with maps in a while, so I was a bit nervous about what to choose. After some research, I decided to use Mapbox GL JS to add the map to my app. I was pleasantly surprised by how easy it was to get started and how powerful the library is.

This is what we will have at the end of this article. An awesome map that's fully interactive and can be styled to match your app's theme or overall goal of the map. Go ahead and play around with it. You can zoom in and out, use ctrl + drag to adjust the pitch and rotation, or shift + drag to scale in and out.

Location

Paris, France

The Eiffel Tower in the evening. This is using the dusk theme. Which is so nice and rich in detail. Later we'll see how to control the lighting and make the map dynamic.

Getting Started

But before we can use the Mapbox API, we need to create an account and get an access token. You can sign up for a free account at Mapbox Account. Once you have an account, you should be able to find your access token right on the account home page.

The free tier is quite generous and gives you 50,000 map views per month, which should be more than enough for most hobby projects.

Once you have your token you can install the Mapbox GL JS library using npm or whatever package manager you prefer. npm install mapbox-gl. And then import it into your React component. For this example I'm using Next.js so the way you access your environment variables is a bit different.

"use client";

import React, { useRef, useEffect } from "react";
import mapboxgl from "mapbox-gl"; // import the mapbox library
import "mapbox-gl/dist/mapbox-gl.css"; // import the mapbox styles
import { useTheme } from "next-themes";

mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN; // set the access token

Creating the Map Component

Next, we create a React component for our map. We use React's useRef hook to create a reference to the map container, and another reference to the map itself. This allows us to access the map instance and the container element in our component.

Also setting the params for the map like the longitude, latitude, zoom level, and pitch. Feel free to add or remove to your components parameters to make it reusable to your needs.

export default function Map({ lng, lat, zoom = 2.5, pitch = 25 }) {
  const mapContainer = useRef(null);
  const map = useRef(null);
  // ...

  return()
}

Initializing the Map

We use the useEffect hook to initialize our map. We only want to initialize the map once, so we return early if map.current is already defined. We create a new map instance and pass in the container element, the center coordinates, the zoom level, and the pitch.

This part is really fun fun because you can play around with the 3D settings and see the map come to life. You can adjust the pitch and zoom level to get the perfect view of your map. Here is the complete API reference for the Map object.

export default function Map({ lng, lat, zoom = 2.5, pitch = 25 }) {
  const mapContainer = useRef(null);
  const map = useRef(null);

  useEffect(() => {
    if (map.current) return; // initialize map only once
    map.current = new mapboxgl.Map({
      container: mapContainer.current,
      center: [lng, lat],
      zoom: zoom,
      pitch: pitch,
    });
    // ...
  });

  return()
}

Applying Themes

MapBox Standard comes with 4 awesome themes that you can use to style your map. These are light, dark, dusk, and dawn. Be sure mapTheme to the theme you want to use.

Here however, I'm using the next-themes library to get the current theme of the app with the useTheme() hook. This way I can set the map theme to match the rest of the website, and to automatically switch between light and dark themes.

let mapTheme;
const { resolvedTheme } = useTheme();

if (resolvedTheme === "dark") {
  mapTheme = "night";
} else if (resolvedTheme === "light") {
  mapTheme = "light";
}
Light map
dark map

One World Trade Center in New York City. The first map is using the light theme and the second map is using the dark theme.

Configuring the Map

Once the map's style has loaded, we set the basemap and padding. The basemap is the underlying map style, and the padding is the space around the map that is reserved for UI elements like the zoom control and the location badge.

useEffect(() => {
  if (map.current) return;
  map.current = new mapboxgl.Map({
    container: mapContainer.current,
    center: [lng, lat],
    zoom: zoom,
    pitch: pitch,
  });

  map.current.on("style.load", () => {
    map.current.setConfigProperty("basemap", "lightPreset", mapTheme);
    map.current.setPadding({ left: 150 });
    // ...
  });
});

Also I'm adding some padding, in order to make space for the location badge that is to push the long and lat slightly to the right, this is for when the map is a rectangle aspect ratio. Otherwise I would remove the padding and center the position of the badge.

Location

Paris, France

Adding a Marker

Finally, I'm adding a marker to the map to indicate my current location. The marker can be any HTML element, but I'm using a simple div with a class name, which is styled using CSS. I just created something simple and recognizable, but you can get creative and make it your own.

map.current.on("style.load", () => {
  map.current.setConfigProperty("basemap", "lightPreset", mapTheme);
  map.current.setPadding({ left: 150 });

  const el = document.createElement("span");
  el.className = "map-marker";
  new mapboxgl.Marker({ element: el }).setLngLat([lng, lat]).addTo(map.current);
});
.map-marker {
  position: absolute;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background-color: var(--blue-10);
  transform: translate(-50%, -50%);
  border: 3px solid rgba(255, 255, 255, 0.95);
}

Location

Paris, France

Conclusion

Lastly, remember to include ref=(mapContainer) within the div that will house the map. This directive enables the map to identify its rendering location, while the class map-container serves as the canvas where Mapbox applies styles and generates the map itself.

Adding a map to a React app can seem daunting at first, but with the right tools and libraries, it can be a fun and rewarding experience. Mapbox GL JS is a powerful library that makes it easy to add beautiful, interactive maps to your app.

Here is the final code for our Map component for reference. I hope this article has helped you get started with adding maps to your React app.

export default function Map({ lng, lat, zoom = 2.5, pitch = 25 }) {
  const mapContainer = useRef(null);
  const map = useRef(null);

  const { resolvedTheme } = useTheme();
  let mapTheme;

  if (resolvedTheme === "dark") {
    mapTheme = "night";
  } else if (resolvedTheme === "light") {
    mapTheme = "light";
  }

  useEffect(() => {
    if (map.current) return;
    map.current = new mapboxgl.Map({
      container: mapContainer.current,
      center: [lng, lat],
      zoom: zoom,
      pitch: pitch,
    });

    // set config properties
    map.current.on("style.load", () => {
      map.current.setConfigProperty("basemap", "lightPreset", mapTheme);
      map.current.setPadding({ left: 150 });

      const el = document.createElement("span");
      el.className = "map-marker";

      new mapboxgl.Marker({ element: el })
        .setLngLat([lng, lat])
        .addTo(map.current);
    });
  });

  return (
    <div
      className="overflow-clip rounded-xl border border-secondary"
      style={{ height: "400px" }}
    >
      <div ref={mapContainer} className="map-container h-full w-full" />
    </div>
  );
}

Our final Map component Map.jsx.

Tags

  • React
  • MapBox GL
  • Next.js

Contact

Questions or need more details? Ping me on Discord, or any of my other social media links.

Newsletter

Get personal updates and readings on topics like tech, design, productivity, programming, and more!

Join the 0 other readers.