Using Leaflet.js in a React Project: Build a Mapping Application

Josh Harris
JavaScript in Plain English
7 min readMar 7, 2023

--

Photo by Tamas Tuzes-Katai on Unsplash

Applaud this story and follow me for more Leaflet- and JavaScript-based content!

Maps are everywhere. Maybe you’ve noticed that just about every website these days has some sort of mapping application included somewhere. Some sites show business locations near you, some show the reach of a group’s community work, and much more.

As a developer, being able to create maps from scratch is a great skill set to have. There are a ton of companies that specialize in GIS software development and are looking for skilled developers. Having a mapping application or two in your portfolio will definitely set you apart from the devs submitting portfolios consisting of to-do lists and calculators.

Not that there’s anything wrong with to-do lists and calculators 😬

Leaflet.js is one of the most popular open-source Javascript libraries for building these interactive mapping applications. From the docs:

Leaflet is designed with simplicity, performance and usability in mind. It works efficiently across all major desktop and mobile platforms, can be extended with lots of plugins, has a beautiful, easy to use and well-documented API and a simple, readable source code that is a joy to contribute to.

Let’s say you have a React-based website (check out this starter I set up for you on Stack Blitz), and you want to add a Leaflet map to showcase your mapping skills. There is actually a React binding for Leaflet, React-Leaflet. The problem is that it won’t do you any good unless you understand how Leaflet itself works inside of a React application.

From the React-Leaflet docs:

React Leaflet provides bindings between React and Leaflet. It does not replace Leaflet, but leverages it to abstract Leaflet layers as React components. As such, it can behave differently from how other React components work.

So, let’s go over how to build a Leaflet map component inside of a React application!

TL;DR: The completed code and map component can be found at the bottom of the page!

Getting Started

We can start by importing all the necessary dependencies at the top of the file, including React, Leaflet, and Leaflet’s CSS. We can also add our map tile instance, some styling for the map, and a div with an id of “map” that will hold our Leaflet map instance. A wide variety of map tiles can be found here if our default tile doesn’t suit your needs.

App.tsx

To create the instance of our Leaflet map, we are going to make use of React’s useEffect hook. We only want to create an instance of the Leaflet map when the component is first mounted, so we are going to provide the useEffect function with an empty dependency array. We also want to pass a set of parameters to the map instance with the type L.MapOptions, which are outlined here in the Leaflet docs. Notice that we are setting the zoomControl property to false as we will be adding a zoomControl manually in a bit.

Now, once we save all the changes to App.tsx, we should have a very basic Leaflet map displayed on our screen.

A bare-bones Leaflet.js map

Accessing the Map Instance with Refs

Now that we have the instance of our map created, we are going to want to be able to access this map to add layers, add controls, and use third-party Leaflet plugins. This is where the ever-useful React Refs come in. While refs are usually used to access DOM elements, you can also use them as a generic container since the current property is mutable and can hold any value, e.g., our map instance. By setting the map instance to a ref, you can access it outside of the useEffect hook and modify it as needed. If you were to set the map instance to useState instead, it would trigger a re-render each time the state is updated, which is not necessary in this case. So, we will instantiate useRef with a null value and assign the ref to the map instance when the map instance is created.

Be sure to notice that we are utilizing the cleanup function of this useEffect hook that creates our leaflet map instance. We want to make sure the cleanup function returns a function that destroys the map instance to avoid potential memory leaks. The Leaflet documentation recommends using the remove method of the map instance to remove the map and all related event listeners. This will ensure that the map instance is properly destroyed should the component unmount.

Adding Controls

Once we have assigned the refs, we can add some basic controls to the map instance so let’s add a zoomControl and a layerControl. For the layerControl, we need to pass a baseLayers object to the control. The baseLayers object will consist of any map tiles you want users to be able to toggle between, like toggling between a light map and a dark map for example. The overlays object is for adding and removing overlay layers like markers and popups to and from the map, but we will worry about this later. From the docs:

The baseLayers and overlays parameters are object literals with layer names as keys and Layer objects as values:

{
"<someName1>": layer1,
"<someName2>": layer2
}

The layer names can contain HTML, which allows you to add additional styling to the items:

{"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}

As for the zoomControl, there are a few options you can pass but for our purposes, we are only going to pass the position property inherited from Control. We can set up a second useEffect function that will handle our control creation logic and keep it separate from our map creation logic.

Map Event Listeners

We can also add map event listeners to the map instance. Again, we’ll be using the mapRef to access our Leaflet map instance and add the desired listener. To keep it simple, let’s add a zoomstart event listener that logs "Zoom Started" to the console.

Adding UI Layers

If you don’t have any data to display and you are content with an empty map, then you are all done! Congratulations and enjoy your map app! For everyone else, however, we are creating our map app to display geographic data. Say we have a list of city data and we want to use Leaflet’s Circle to show these cities. Paste the following list of cities in a new file, data.ts, in the src directory:

data.ts

Now, we need to import the data and create a layerGroup to which we are going to add our Circle city markers. We are going to set up a ref for the layerGroup, then add that layerGroup to the map instance. Then, we can use .forEach() on the cityData array to create a Circle for each city and add it to the map instance.

We also want to be able to toggle the newlayerGroup from our layer control. How do we do this? We use another ref, of course! We will set up a ref for the layerControl so that we can use its addOverlay method to add our new layerGroup to the control.

Wrap up

The final product… A map with circles!

Now, we have a basic, yet functional Leaflet map application. You can see how coding this way can get repetitive fast, with our use of multiple useEffect and useRef hooks. This is where React-Leaflet comes in as it takes care of much of the logic found in our useEffect hooks behind the scenes. Our code will also look much more like a typical React component as opposed to a single Map div with a bunch of useEffect hooks handling layer creation. You will find that we will still use the useRef hook a lot since Leaflet is doing the DOM rendering and we will need to access those Leaflet elements in the DOM. But, that’s for next time.

I encourage you to read through the Leaflet docs and to experiment with some of the third-party plugins. You will find there is a ton of cool functionality that can be added to your map.

Applaud this story and follow me for more Leaflet- and JavaScript-based content!

Thanks for reading and check out some of my other articles regarding React and Leaflet.js:

Working Demo on Stack Blitz

More content at PlainEnglish.io.

Sign up for our free weekly newsletter. Follow us on Twitter, LinkedIn, YouTube, and Discord.

Interested in scaling your software startup? Check out Circuit.

--

--