Integrating Next.js with Leaflet.js + Mapbox

tsaxena4k

Tushar saxena

Posted on December 5, 2020

Integrating Next.js with Leaflet.js + Mapbox

Do you want to include interactive maps in your Nextjs application?Then you must have come across Leafletjs. 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.

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
Enter fullscreen mode Exit fullscreen mode

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 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

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 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. 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.

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 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>
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

And that's it you're good to go with something like this 👇

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!👍

💖 💪 🙅 🚩
tsaxena4k
Tushar saxena

Posted on December 5, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related