Integrating Next.js with Leaflet.js + Mapbox
Tags: nextjs, leafletjs, react, mapbox
Published: 2020-12-05 08:16:21

**Do you want to include interactive maps in your [Nextjs][1] application?**Then you must have come across [Leafletjs][2]. Though Leafletjs is very simple to use, but when it comes to Server Side rendered(SSR) applications build with Nextjs it lacks a little which can be annoying at times. But don't you worry I've found a workaround for this. 

To set the scene let us first know 👇
## **Why Leafletjs?**
Leaflet is the leading `open-source` JavaScript library for `mobile-friendly` interactive maps. Weighing just about 39 KB of JS, it has all the `mapping features` most developers ever need.

While Leaflet is meant to be as lightweight as possible, and focuses on a core set of features, an easy way to extend its functionality is to use third-party plugins. Thanks to the awesome community behind Leaflet, there are literally hundreds of nice plugins to choose from. We'll use one of those plugins in an example later in this post.

## **Tutorial**
Please note that in this tutorial, I am making the assumption that you already have an existing Next.js project up and running. If you don’t, please start by traversing the [Next.js documentation][1].

### **Install the required dependencies**
`npm i leaflet leaflet-defaulticon-compatibility leaflet-geosearch react-leaflet`

**Note**: if you use TypeScript, make sure you install `@types/leaflet` otherwise you'll get compile errors on certain attributes used in the example.

I'll explain the requirement of each as we use them further in the tutorial.

### **Creating the map component**
In your application create a **map.jsx** file inside the component folder `./component/Map.jsx`.

It is important that this code is in a separate file from where it is embedded into your page because otherwise you'll get a *window undefined error* about which we'll talk later.

**Side note**: For Typescript users the file is called as map.tsx.
Inside the file put the following code 
```
import { MapContainer, TileLayer,Marker,Popup } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css'
import "leaflet-defaulticon-compatibility";

const Map = () => {
  return (
    <MapContainer center={[40.8054,-74.0241]} zoom={14} scrollWheelZoom={false} style={{height: "100%", width: "100%"}}>
      <Marker 
      position={[40.8054,-74.0241]}
      draggable={true}
      animate={true}
      >
        <Popup>
          Hey ! you found me
        </Popup>
      </Marker>
    </MapContainer>
  )
}

export default Map
```
In the above example I have used some basic attributes from react-leaflet to initialize the map.
* `center`: centers the map around the provided latitude & longitude.
* `zoom`: Initial zoom for the Map ranging from 0 to 18.
* `scrollWheelZoom`: yes it is exactly what it sounds like.
* `position`: sets the position for the Marker.
* `draggable`: helps drag and drop your marker on map.
* `animate`: if true, panning will always be animated.

There are lot many features and examples available in [react-leaflet][4] documentation.

### **Setting up Mapbox API**

In the above example, we will be using Mapbox API to add custom title layer to our map.
Mapbox plugin is quietly supported by leaflet and it also provides you with many custom mapping styles, you can even create your very own styles in their studio, for this part of the tutorial will use the default styles.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/log8y0euh7du6r12u3sf.jpg)

The first thing we’ll need to set up our custom Mapbox style is to have an account. I'm not going to walk you through that process, but you can head over to [Mapbox’s website](https://www.mapbox.com/) where you can sign up for free.

**To generate a token that we’ll use for providing access to our Map.**
* Head on over to the Account section of the Mapbox dashboard which you can access by clicking over your profile in the top right section of the navbar.
* Mapbox provides you with a “default” token that you can use in your applications. You're free to use this, but I recommend creating a new token that you can provide a unique name.

**Configuring our custom endpoint**
For this tutorial, we’re going to use [Mapbox’s Static Tiles service](https://docs.mapbox.com/api/maps/static-tiles/). You can copy the endpoint from there which will look like this.

`https://api.mapbox.com/styles/v1/{username}/{style_id}/tiles/256/{z}/{x}/{y}@2x?access_token={access_token}`

There are a few parameters here we need to understand:

* `username`: this will be your Mapbox account’s username
* `style_id`: this will be the ID of the style you are using
* `z, x, y`: these are parameters that Leaflet programmatically swaps out, so we want to leave them as is
* `access_token`: this is the Mapbox key you created above

For this part of the example, we are using styles provided by Mapbox itself. You can also create your very own styles in Mapbox but for now, will use the `streets-v11` from [here](https://docs.mapbox.com/api/maps/styles/).

And once I update my endpoint parameters, the final tilepoint URL will look like:
`https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}@2x?access_token=MY_ACCESS_TOKEN`

*Since the style is provided by mapbox thus username in the URL is replaced with mapbox, if you are using your own style then you'll replace it with your own username.*

### **Adding a custom TileLayer to React Leaflet**
Inside of your `<MapContainer>` component in map.jsx you include a `<TileLayer>` component, which defines the imagery of the world that you base your map upon.

The example on the React Leaflet homepage uses a public version of [OpenStreetMap](https://www.openstreetmap.org/#map=4/21.84/82.79) as their TileLayer, which is an open source map project created and updated by people all around the world.
```
<MapContainer center={position} zoom={13}>
  <TileLayer
    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors"
  />
</MapContainer>
```
This gives you a basic map, but we want to swap in Mapbox so we can set up a custom look and feel for our map.

To add our custom style, we’ll want to update the `url` and `attribution` props of the TileLayer component.

For URL, it will simply be the custom style endpoint we created earlier, so in my example, it looks like:

`https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}@2x?access_token=MY_ACCESS_TOKEN`

For attribution, we want to credit Mapbox as the service, so we want to set our attribution as:

`Map data &copy; <a href=&quot;https://www.openstreetmap.org/&quot;>OpenStreetMap</a> contributors, <a href=&quot;https://creativecommons.org/licenses/by-sa/2.0/&quot;>CC-BY-SA</a>, Imagery &copy; <a href=&quot;https://www.mapbox.com/&quot;>Mapbox</a>`

When plugged in to our TileLayer, our map.jsx should now look like this:
```
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
import "leaflet-defaulticon-compatibility";

const Map = () => {
  return (
    <MapContainer
      center={[40.8054, -74.0241]}
      zoom={14}
      scrollWheelZoom={false}
      style={{ height: "100%", width: "100%" }}
    >
      <TileLayer
        url={`https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}@2x?access_token=MY_ACCESS_TOKEN`}
        attribution='Map data &copy; <a href=&quot;https://www.openstreetmap.org/&quot;>OpenStreetMap</a> contributors, <a href=&quot;https://creativecommons.org/licenses/by-sa/2.0/&quot;>CC-BY-SA</a>, Imagery &copy; <a href=&quot;https://www.mapbox.com/&quot;>Mapbox</a>'
      />
      <Marker position={[40.8054, -74.0241]} draggable={true} animate={true}>
        <Popup>Hey ! I live here</Popup>
      </Marker>
    </MapContainer>
  );
};

export default Map;
```
### **Finally let's render our Map**
As you might know, the global `window` object is not available in SSR, you'll get a ReferenceError if you try using it there.
Now to avoid this we can take advantage of Nextjs's dynamic loading which will help to prevent SSR.
Inside `./pages/index.js` emmbed your Map component like this:
```
import React from "react";
import dynamic from "next/dynamic";

export default function Home() {
  const MapWithNoSSR = dynamic(() => import("../component/map"), {
    ssr: false
  });

  return (
    <main>
      <div id="map">
        <MapWithNoSSR />
      </div>
    </main>
  );
}
```
**And that's it you're good to go with something like this 👇**

{% codesandbox m7qnb %}

## **Closing thoughts**

I hope this quick tutorial was helpful to you in some way 😊. I know it would have saved me quite a bit of work if I had this before I went down my Next.js + leafletjs path. Once you’ve got it all working, Don't forget to provide me with your valuable feedback. Good luck!👍

[1]:https://nextjs.org/
[2]:https://leafletjs.com/
[3]:https://docs.mapbox.com/api/maps/#styles
[4]:https://react-leaflet.js.org/docs/start-introduction