Using Leaflet.js in a React Project: Build a Mapping Application
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.
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.
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
andoverlays
parameters are object literals with layer names as keys andLayer
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
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:
- React-Leaflet v3: Creating a Mapping Application
- Building Interactive Mapping Applications with Leaflet.js and Vue’s Options API
- How to Create a Custom Layer Component in React-Leaflet v3 with Leaflet.Ellipse
- How to Create a React-Leaflet Control Component with Leaflet-Routing-Machine
- How to Create a Custom Geoman Control with React-Leaflet and Leaflet-Geoman-Free
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.