Creating a React-Leaflet Custom Component Using Hooks
By: Lucas Andión
Tags: Maps, Leaflet, React Hook, Unesco Heritage Sites, React

Creating a React-Leaflet Custom Component Using Hooks

Photo by Timo Wielink on Unsplash

In my previous post I showed how easy it is to set up your own maps using React-Leaflet, and even drop some markers and popups on them.

Leaflet provides every basic map functionality you need but it’s meant to be as lightweight as possible. To go beyond that there are hundreds of third-party plugins providing different tiles, ways of loading and displaying external data, custom markers and paths, etc.

React-Leaflet provides the same core functionality but does not support any Leaflet plugin. There are a few third party plugins ported to React-Leaflet, but most of the ecosystem present in Leaflet is missing.

The recommended approach when you need extra functionality is to create your own React-Leaflet components.

Custom Components

To create your component, you will have to extend the appropriate abstract class from the ones exposed by React-Leaftlet.

You can extend:

MapControl

Meant for controls, handles positioning.

Meant for controls, handles positioning. ControlledLayer

Base class to handle adding and removing layers.

Base class to handle adding and removing layers. MapEvented

Base class that handles event bindings. Except MapControl and ControlledLayer , the rest of the base components inherit from this one. That includes the Map component, the top-level component that instantiates a Leaflet map and provides it to its children.

Base class that handles event bindings. Except and , the rest of the base components inherit from this one. That includes the component, the top-level component that instantiates a Leaflet map and provides it to its children. MapComponent

Base class handling map panes. Panes are DOM elements used to control the ordering of layers on the map.

Base class handling map panes. Panes are DOM elements used to control the ordering of layers on the map. MapLayer

Base class extending MapComponent , handles adding and removing the layer to the map when relevant. Exposes the layer in layerContainer .

Base class extending , handles adding and removing the layer to the map when relevant. Exposes the layer in . GridLayer

For creating tile layers, handles opacity and z-index .

For creating tile layers, handles and . DivOverlay

Handles shared logic for Popup and Tooltip components.

Handles shared logic for and components. Path

Extends MapLayer with methods to style paths.

There’s not a lot of examples of custom components out there, but most of what I’ve seen that does not have to do with tile layers extend MapControl .

MapControl handles positioning and it’s a good fit if what you are trying to show on your map does not need to fill all of it or depends on a geographical position.

Our Example

Following our previous example we will be creating a map that shows a list of markers with Unesco Heritage sites and shows a legend on the bottom of the map when we select one instead of the classic popup.

Most of our components will re-use the existing ones, we will have a HeritageMap , using our existing MyMap component, but this time using only one provider. The stamen watercolor provider with its hand-made look seems like a nice choice for this.

Our HeritageMap will receive a list of heritage sites and print a marker for each of them. No custom components here, just reusing the existing ones.

A Leaflet map knows what area to show using either:

The bounds prop: two geographical points defining a square that has to fit inside the map.

prop: two geographical points defining a square that has to fit inside the map. A center point and zoom value.

When there is no Marker selected we calculate the map bounds using Leaflet’s latLngBound . By extending the bounds with the positions of all the markers we get a a final result that includes them all so they are all shown on the map when loaded. Finally we set a little padding using pad(0.1) so there are no markers just on the borders of the map.

Galicia’s Unesco World Heritage sites.

When of the markers is selected, we set the center to its position and use the default zoom of 14 .

The Custom Component

Our legend with the marker information will be a custom Leaflet Control.

Why a Control? Because it’s the only extendable element that it’s meant to be on our screen all the time and not positioned on the map or around a Marker .

We use an useEffect to add the control when our component is mounted. The control will be positioned to the bottomleft corner of the map and contain a div with the legend and its own styling (a pirate font seemed on point). We also have to define the required onAdd() method to return the HTMLElement that will fill the control. Note this is a string representation of a DOM element and not JSX.

All that is left it’s to be sure to clean up the control on unmount returning a function that calls its remove function.

But, but… you said to extend the appropriate React-Leaflet abstract component!

Yes! But while writing the example using React Hooks I found this way easier and more readable. Then I found Paul le Cam has a v3 on its way which changes the available hooks and the way of working with them. Maybe this was one of the reasons.

The previous “what component to extend from” list is still valid. Just use the wrapped original Leaflet component and you can extend them this way and use them on your React-Leaflet maps.

Now that we have our HeritageLegend component we can just use it in our map.

The HeritageLegend component inside our MyMap. Full example here.

After all that, we can click on any of our Markers and: Ahoy! The map will be centered around it, zoomed and its information will be shown like this:

Roman Walls of Lugo ahoy!

Summing up

As you can see it is possible to extend Leaflet controls to create your own and use them inside React-Leaflet.

There’s not a lot of documentation on using React-Leaflet with hooks. Its v2 docs just mentions the useLeaflet hook as we use it here, and there isn’t a lot of examples using it online, or combining it with React Hooks.

My recommendation would be to wait until v3 comes out, and meanwhile create your own components like this. It is not clear when will it be released, but you can follow the next branch on its Github repository.