React-Leaflet v3: Creating a Mapping Application

Josh Harris
JavaScript in Plain English
8 min readMay 3, 2021

--

Thanks for the view, NASA!

If you have read my article ‘Using Leaflet.js in a React project: Build a Mapping Application’, then you know that we can create a fully functional Leaflet map in a React project without using the React-Leaflet binding. If you haven’t read it, check it out!

Here’s a look at the code for the Leaflet component that we created:

This works just fine, but our code isn’t very React-ish since we are manipulating the DOM with vanilla Leaflet and rendering everything to a single “map” div. Let’s go over how to create a proper open-source React mapping application with React-Leaflet.

Some Things To Remember

It's important to remember that React-Leaflet is not replacing Leaflet. It’s simply providing a way to create Leaflet layers as React components, rather than using a useRef/useEffect setup like we used in our vanilla-Leaflet/React app. This means that our React-Leaflet components may behave differently from a standard React component. From the React-Leaflet docs:

DOM rendering

React does not render Leaflet layers to the DOM, this rendering is done by Leaflet itself. React only renders a <div> element when rendering the MapContainer component, the contents of UI layers components.

Remember how we rendered everything to a single div in the vanilla Leaflet map? React-Leaflet is doing the same thing behind the scenes.

Component properties

The properties passed to the components are used to create the relevant Leaflet instance when the component is rendered the first time and should be treated as immutable by default.

During the first render, all these properties should be supported as they are by Leaflet, however they will not be updated in the UI when they change unless they are explicitly documented as being mutable.

Mutable properties changes are compared by reference (unless stated otherwise) and are applied calling the relevant method on the Leaflet element instance.

Again, this is quite similar to our vanilla Leaflet map. The leaflet elements are instantiated only when the component is first mounted, the same way we used the useEffect hook, then later modified using refs.

React context

React Leaflet uses React’s context API to make some Leaflet elements instances available to children elements that need it.

Each Leaflet map instance has its own React context, created by the MapContainer component. Other components and hooks provided by React Leaflet can only be used as descendants of a MapContainer.

React-Leaflet uses the context API to make its instances and state available to all of its children. You’ll see an example of this once we get to how we handle map events.

Getting Started

To make setup easy, I have a starter for you to use on Stack Blitz. If you prefer to follow along on your local machine, check out the installation section of the docs. Remember to install Leaflet as well as React-Leaflet! Also notice that we will be using some styling and components from Material-UI in this demo. You can also clone this GitHub repo of the finished code.

In public/index.html, we can paste the CDN link for Leaflet’s CSS and Javascript files:

index.html

Now, we can set up our MapContainer and our map tile layer. The MapContainer is what creates the Leaflet map instance and shares that instance with all of its children via the context API. For our purposes, we are only going to pass the center, zoom, style, and zoomControl props to the MapContainer for now, but check out this list of props you can pass in for your later use. We’ll be centering our map over the United States ([37.0902, -95.7129]), setting the zoom to 3, and setting zoomControl to false as we will be adding one in a bit. The style prop also needs to have the height and width set.

In src/map we can create a new file named Map.jsx, then import it into our App.js file:

Map.jsx

App.jsx

You should now have an empty map on your screen that looks something like this:

Excited yet?

Adding Controls

Now that we have our map up and running, let’s add a ZoomControl and a LayersControl.

The ZoomControl is super easy. We need to import the ZoomControl component and place it just under the TileLayer component. We also want to pass a position prop to it with the value 'topright':

Map.jsx

The LayersControl is a bit different. We can import this component and place it right above the ZoomControl that we just set. Then, we use LayersControl.BaseLayers and LayersControl.Overlay to add our layers to the control. We’ll use the LayersControl.Overlay component to toggle layers like Markers and Circles on and off of the map later, but for now, we’ll put the TileLayer component inside of the LayersControl.BaseLayers component. Let’s also add a second map tile option so we can actually use our layer control. Head to the Leaflet-Providers demo and pick a second tile to use, then set up your code like so:

Map.jsx

Sweet controls, bro.

For further reading on this, check out this article on how to create custom control components using the React-Leaflet Core API.

Accessing The Map Instance & Event Listeners

Think back to the Things To Remember section of this article. We learned that the MapContainer makes the Leaflet map instance available to its children and that the hooks to access that state are only available to its descendants. Because of this, it is beneficial for us to separate the MapContainer logic from the layer logic. In src/map, let's create a new file called Layers.jsx and copy/paste our LayersControl component here:

Layers.jsx

Map.jsx

The Leaflet map instance has a bunch of methods available for getting and setting its state. To use these methods, React-Leaflet provides us with the useMap hook. Say that we want to get the initial bounds and zoom level of our map. We would call useMap at the top of our component and assign it to a variable, let's say map, then refer to that variable whenever we want to use a Leaflet map method:

Layers.jsx

If we wanted to get the bounds and the zoom level every time they changed, however, we would use the useMapEvents hook. We import it the same way as the useMap hook, but we will attach the Leaflet map event handlers to the map instance like this:

Layers.jsx

Displaying Data with GeoJSON

There’s a very good chance that at some point in the building process of your mapping application you will be fetching and displaying data in geoJSON format. If you aren’t familiar with geoJSON formatting, start HERE. Our map is currently centered over the USA, so we’ll be using the geo-data for the states Wyoming, Montana, and North & South Dakota. I’ve already set up the files containing the geoJSON for each state in src/data.

To display the geoJSON, we are going to use the GeoJSON React-Leaflet layer component. We are going to import the data to Layers.jsx, put the data in an array in state, then use .map() to iterate that array and return the GeoJSON layers. When iterating the data, we need to pass the geometry property found in the features array of each set of geoJSON, which gives the GeoJSON layer the type and coordinates of the layer to create. We will also need to provide a unique key to each GeoJSON layer, so we’ll use display_name found in the properties of each features array. If you want to alter the style of the GeoJSON layer, you can pass an options object to the pathOptions prop. Here’s how all this is going to look:

Layers.jsx

Those are some good lookin’ states, right there.

Adding Overlays to the LayersControl

To add the baselayers to our LayersControl, we used LayersControl.BaseLayer for each base layer that we wanted to add to the map. It's a very similar process to add the desired overlays. Rather than LayersControl.BaseLayer, we’ll wrap our layers in LayersControl.Overlay providing a name and setting checked equal to true so the layers appear on the map by default:

Layers.jsx

Montana & South Dakota: A Love Story

Adding Markers and Popups with LayerGroup

Let’s say that we want to add more to our map’s UI by displaying some Markers to go along with the geoJSON. Creating the point-markers is incredibly easy. We just import React-Leaflet`s Marker layer component, wrap it with the Geojson layer component, and pass a latLng object that we will create with Leaflet’s LatLng method to its position prop. We’ll also import the Popup layer component, add some text, and wrap that with the Marker component. We can also use libraries like Material UI to add some extra style to our Popups. For now, we’ll just use the Material UI Typography and Divider components:

Layers.jsx

Montana is super cool, I swear.

Notice anything weird, specifically with the LayersControl? Each name is showing up twice. This is because the LayersControl is adding a toggle for both the border and the point-marker with the same name, which is something we don’t want to happen. To fix this, we just have to tell the LayersControl that the GeoJson and the Marker components are part of a LayerGroup. Just import the React-Leaflet LayerGroup component, and use it as a wrapper from the GeoJSON and Marker:

Layers.jsx

I heard Kanye lives around here somewhere…

Layer Event Handlers

There will be times that we want to modify the elements on our map, like changing the fillOpacity of the GeoJSON with the onmouseover event and again onmouseout. To add this functionality, we would use the eventHandlers prop of the GeoJSON:

Layers.jsx

Wrap Up

Ok, we now have a working mapping application built with React-Leaflet version 3! Here’s the final version of our code (GitHub):

Layers.jsx

Map.jsx

I encourage you to read through the docs as there is a bunch of cool stuff you can do with this package.

After you’ve had some practice using all of the hooks and components that the React-Leaflet Public API has to offer, head over to the Core API section of the docs. The Core API lets you create custom Leaflet components and extend third-party Leaflet plugins to work with React-Leaflet.

To help you understand how the Core API works, check out this article I wrote showing you how to create a custom Ellipse component with Leaflet.Ellipse. You can also check out this article that shows you how to create custom control components with Leaflet-Routing-Machine.

If you liked this article, feel free to leave a clap and follow me to keep up with my future articles. You can also connect with me on LinkedIn. Thanks for reading!

More content at plainenglish.io

--

--