How to use Mapbox in Next.js
Published: 2023-05-27 13:15:00

## **Table of content**

1. [Mapbox](#mapbox)
   - [Setting up the Mapbox key](#setting-up-the-mapbox-key)
2. [Next.js](#nextjs)
   - [Dependencies](#dependencies)
   - [Adding key to Next.js configuration](#adding-key-in-nextjs-environment)
3. [Initializing the map](#initializing-the-map)
4. [Adding styles](#adding-styles)
5. [Adding the map controls](#adding-the-map-controls)
6. [Loading the data](#loading-the-data)
7. [Adding the custom markers](#adding-the-custom-markers)
8. [Adding the marker popup](#adding-the-marker-popup)
9. [Conclusion](#conclusion)

## Mapbox

At the time of writing this article, to use **Google Maps** API, you need billing information (even though it is free for certain limits). So, **Mapbox** can be a better alternative to **Google Maps** which provides, **50,000** free map loads for the web (without billing credentials). It provides a range of APIs, SDKs, and developer tools for maps, navigation, and search across platforms.

### Setting up the Mapbox key

- Goto [Mapbox](https://www.mapbox.com/) and create an account or login if you have one.
- In your account dashboard, under **Access tokens** section, select **Create a token** button.
- In **Create an access token** page, name your token and hit **Create token**.
- While creating token you can also add different types of token restrictions and url restriction. But for current app we will simply use the default settings.

## Next.js

In this article, we are using **Next.js 13** for our app. We opted for **App Router** for our app instead of **Page router**. Thus, the **src/app** directory will contain our root route _example.com/_. The **page.js** file will define a unique page for each route, and the **layout.js** file will define the UI shared between multiple pages like navigation, header, and footer. We can also nest layouts.

But for simplicity, we will use the root route only. Thus, our initial file structure looks like this:

```
  src
  ├─ app/
  │  ├─ globals.css
  │  ├─ layout.js
  │  ├─ page.js
  package-lock.json
  package.json
```

### Dependencies

In this app, we will use **react-map-gl** and **react-icons**. We will use the **react-map-gl** package to use Mapbox components in the Next.js app.

To install the dependencies run:

`npm install react-map-gl react-icons --save`

### Adding key in Next.js environment

We will add the access token we created in the above section as an environment variable in our Next.js app. In your Next.js root folder, create a **.env.local** file (if you don't have one) and use the prefix `NEXT_PUBLIC_` with the variable name.

```
# .env.local
NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=<Your_Mapbox_Access_Token>
```

## Initializing the map

In the **page.js** file, we will import the **Map** component from the **react-map-gl** package. The **Map** component will render the map. But first, we need to supply the following values to initialize the **Map** component:

1. **mapboxAccessToken**: This attribute takes in the Mapbox access token that we had stored in the **.env.local** file. We can access this token using the `process.env` global variable.
1. **mapbox-gl.css**: This file consists of all the styling for our **Map** component and its child elements. Failing to import this file will result in the inaccurate rendering of the **Map** component and its elements like Marker, Popup, Navigation Controls, etc.
1. **initialViewState**: This attribute takes in the initial location (latitude, longitude, and zoom level). When the map first loads this location will be rendered.

```js
// page.js
"use client";

import Map from "react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";

import classes from "./Page.module.css";

export default function Home() {
	const mapboxToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;

	return (
		<main className={classes.mainStyle}>
			<Map
				mapboxAccessToken={mapboxToken}
				mapStyle="mapbox://styles/mapbox/streets-v12"
				style={classes.mapStyle}
				initialViewState={{ latitude: 35.668641, longitude: 139.750567, zoom: 10 }}
				maxZoom={20}
				minZoom={3}
			></Map>
		</main>
	);
}
```

## Adding styles

In **src/app/globals.css** add the following style:

```css
/* globals.css */

body {
	font-family: Arial, Helvetica, sans-serif;
}
/* Overrides the original padding of the Popup component in the Map */
.mapboxgl-popup-content {
	padding: 0 !important;
}
```

Create a **src/app/Page.module.css** file and add the style for map components:

```css
/* Page.module.css */

.mainStyle {
	max-width: 100%;
	height: 100vh;
}
.mapStyle {
	width: 100%;
	height: 100%;
}
.popupTitle {
	background-color: #87bd41;
	font-weight: 700;
	font-size: medium;
	color: ivory;
	padding: 10px;
}
.popupInfo {
	font-weight: 400;
	font-size: 14px;
	padding: 10px;
}
.popupLabel {
	font-weight: bold;
}
.popupWebUrl {
	color: dodgerblue;
}
.popupWebUrl:active,
.popupWebUrl:focus {
	outline: 0;
}
```

## Adding the map controls

The map controls generally include Navigation controls and Geo-location controls. Under Navigation controls, we get zoom-in, zoom-out, and compass controls. Under Geo-location controls, we get user location control.

```js
// page.js
"use client";

import Map, { NavigationControl, GeolocateControl } from "react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";

import classes from "./Page.module.css";

export default function Home() {
	const mapboxToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;

	return (
		<main className={classes.mainStyle}>
			<Map
				mapboxAccessToken={mapboxToken}
				mapStyle="mapbox://styles/mapbox/streets-v12"
				style={classes.mapStyle}
				initialViewState={{ latitude: 35.668641, longitude: 139.750567, zoom: 10 }}
				maxZoom={20}
				minZoom={3}
			>
				<GeolocateControl position="top-left" />
				<NavigationControl position="top-left" />
			</Map>
		</main>
	);
}
```

Our initial map looks like this:

![Initial Map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzl4c9e5vn0gbnqtg4t5.png)

## Loading the data

For illustration, we will use some airport data. It is not structured in standard geospatial formats like GeoJSON, KML, OSM etc. It is represented in simple JSON format, but it works for now. In the project root, create a **dummyData** folder with the **airports.json** file. We are using partial data on airports in Japan, which is available [here](https://gist.github.com/sakshamgurung/ded1aeae24ff539b7147237c746f03c6). You can get complete data from [here](https://gist.github.com/tdreyno/4278655).

Import by adding `import airports from "../../dummyData/airports.json";` in import section of the **page.js** file. Our project structure will look like this:

```
  dummyData
  ├─ airports.json
  src
  ├─ app/
  │  ├─ globals.css
  │  ├─ layout.js
  │  ├─ page.js
  │  ├─ Page.module.css
  package-lock.json
  package.json
```

## Adding the custom markers

We will use the `Array.map()` function to iterate through each airport's information from the **airports.json** file. In each iteration, we will return the **Marker** component from the **react-map-gl** package and each **Marker** will represent the airport data (point of interest) on the map.

To customize the map marker, we will provide icons from the **react-icons** package as **button** to the **Marker** component. The `onClick()` event on the button will set the `selectedMarker` state and add **flyTo** animation to the map. To animate the map, we will use the `useRef` React hook.

```js
// page.js
"use client";
import { useState, useRef } from "react";

import Map, { Marker, Popup, NavigationControl, GeolocateControl } from "react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";
import { IoMdAirplane } from "react-icons/io";

import airports from "../../dummyData/airports.json";
import classes from "./Page.module.css";

export default function Home() {
	const mapboxToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;
	const [selectedMarker, setSelectedMarker] = useState(null);
	const mapRef = useRef(null);

	const zoomToSelectedLoc = (e, airport, index) => {
		// stop event bubble-up which triggers unnecessary events
		e.stopPropagation();
		setSelectedMarker({ airport, index });
		mapRef.current.flyTo({ center: [airport.lon, airport.lat], zoom: 10 });
	};

	return (
		<main className={classes.mainStyle}>
			<Map
				ref={mapRef}
				// attributes...
			>
				{/*Geolocate and Navigation controls...*/}

				{airports.map((airport, index) => {
					return (
						<Marker key={index} longitude={airport.lon} latitude={airport.lat}>
							<button
								type="button"
								className="cursor-pointer"
								onClick={(e) => zoomToSelectedLoc(e, airport, index)}
							>
								{<IoMdAirplane size={30} color="tomato" />}
							</button>
						</Marker>
					);
				})}
			</Map>
		</main>
	);
}
```

## Adding the marker popup

Marker popup can be useful to provide comprehensive information about the point of interest in our map. When a marker is selected, we will use React state to toggle the popup for that particular marker.

```js
// page.js
"use client";
import { useState, useRef } from "react";

import Link from "next/link";

import Map, { Marker, Popup, NavigationControl, GeolocateControl } from "react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";
import { IoMdAirplane } from "react-icons/io";

import airports from "../../dummyData/airports.json";
import classes from "./Page.module.css";
export default function Home() {
	// functions...

	return (
		<main className={classes.mainStyle}>
			<Map
			// attributes...
			>
				{/*Geolocate and Navigation controls...*/}
				{/*Airport Markers...*/}

				{selectedMarker ? (
					<Popup
						offset={25}
						latitude={selectedMarker.airport.lat}
						longitude={selectedMarker.airport.lon}
						onClose={() => {
							setSelectedMarker(null);
						}}
						closeButton={false}
					>
						<h3 className={classes.popupTitle}>{selectedMarker.airport.name}</h3>
						<div className={classes.popupInfo}>
							<label className={classes.popupLabel}>Code: </label>
							<span>{selectedMarker.airport.code}</span>
							<br />
							<label className={classes.popupLabel}>Country: </label>
							<span>{selectedMarker.airport.country}</span>
							<br />
							<label className={classes.popupLabel}>Website: </label>
							<Link
								href={selectedMarker.airport.url === "" ? "#" : selectedMarker.airport.url}
								target={selectedMarker.airport.url === "" ? null : "_blank"}
								className={classes.popupWebUrl}
							>
								{selectedMarker.airport.url === "" ? "Nil" : selectedMarker.airport.url}
							</Link>
						</div>
					</Popup>
				) : null}
			</Map>
		</main>
	);
}
```

Our final map looks like this:

![Final Map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16tfikflyw5uobodk9ih.png)

## Conclusion

In this article, we created a map application in Next.js using the **Mapbox** API. We rendered the map and plotted the airport location with additional information using the popup.

You can add many features to the application and create fascinating map applications. We will explore these features in the upcoming articles. Until then, happy coding.

Thanks for reading this article. Don't forget to check out my [other articles](https://recodebrain.com/browse). You can support this article by sharing it. Bye 👋