Tutorial: React ⚛ + Leaflet 🗺
Tags: javascript, react, tutorial, leaflet
Published: 2021-01-14 17:22:57

Hello, 👋 Today I want to share with you a small tutorial on how to use Leaflet with React.

For this, we are going to make a web app where we can find some tourist places at... Paris! 🇫🇷 🥖.

> ### You can check the Demo on :  [https://react-leaflet-example.netlify.app/](https://react-leaflet-example.netlify.app/) 🚀 


### What is Leaflet?
Leaflet is the most popular open-source library for interactive maps. You can add markers to a map, make a circle, square, line, and much more interesting stuff. Check the documentation for more:  https://leafletjs.com/

### What we are going to do : 📝
* Create our project and install all dependencies
* Show a map, centered on Paris
* Show markers on the map
* Add a new marker on the map
* Draw line between markers

> For a sake of visibility, I will put only some part of the code in this article, but feel free to check the source code on [Github repo](https://github.com/Maj07/react-leaflet-example)

### Installation ⚙️
First of all, let's create and install dependencies for this project.

1. Let's create our project with [create-react-app](https://github.com/facebook/create-react-app)
```bash
npx create-react-app react-leaflet-example --template typescript
```
2. Install [leaflet](https://leafletjs.com/) and [react-leaflet](https://react-leaflet.js.org/)
```bash
npm i -S leaflet react-leaflet
```
3. Install types (Optional if you are not using typescript):
```bash
npm i -D @types/leaflet
``` 

### Show a Map 🗺
Now that we have installed all we need, let's create a new component called Map. And add the starter code from the documentation.  
It uses MapContainer, and we are passing to it some coordinates to center the map to our city.

> TileLayer is used to show copyright on the Map

```tsx
import { MapContainer, TileLayer } from "react-leaflet";

const Map = () => {
  const defaultPosition: LatLngExpression = [48.864716, 2.349]; // Paris position

  return (
    <div className="map__container">
      <MapContainer
        center={defaultPosition}
        zoom={13}
      >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
      </MapContainer>
    </div>
  );
};
```
![screenshot](https://dev-to-uploads.s3.amazonaws.com/i/55e1e98ch6ddwzu0opdt.png)


### Show markers on the map 📍
Our map doesn't have any places yet.  So let's add some default places. For that, I created an array of places in advance, with those attributes: 
```ts
import { LatLngExpression } from "leaflet";

export interface Place {
  id: string;
  picture: string;
  title: string;
  description: string;
  seeMoreLink: string;
  position: LatLngExpression;
}
```
> In my application, I use redux to handle state management of places, show preview modal, etc. I skip how to create/use a store with react-redux. You can check the code on [githup repo]()

So let's import our array of places, and use the Marker component for each place. Marker needs as props our place's position. We can also handle events like click to show a place's description.  
Check more about [Marker's options](https://react-leaflet.js.org/docs/api-components/#marker)

```tsx

import { MapContainer, TileLayer, Marker, Tooltip } from "react-leaflet";
import { LatLngExpression } from "leaflet";

const Map = ({places}) => {
  const defaultPosition: LatLngExpression = [48.864716, 2.349]; // Paris position

  const showPreview = (place) => {
    // show place's description
  }

  return (
    <div className="map__container">
      <MapContainer
        center={defaultPosition}
        zoom={13}
      >
        {places.map((place: Place) => (
          <Marker
            key={place.id}
            position={place.position} // 👈
            eventHandlers={{ click: () => showPreview(place) }}
          >
            {/* show place's title on hover the marker */}
            <Tooltip>{place.title}</Tooltip> 
          </Marker>
        ))}

        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
      </MapContainer>
    </div>
  );
};
```

![screenshot](https://dev-to-uploads.s3.amazonaws.com/i/3yupc8gsvc8pnlfsm38v.png)


### Add new marker on the map ➕📍
Cool! Now we have markers on our map, but how to add new places dynamically? Let's say we want to add a new place when we click on the map.

To show a new place on click, we are going to create a new component called AddMarker that will handle this and update the store.  
In this component, we are going to use [useMapEvents](https://react-leaflet.js.org/docs/api-map/#usemapevents) hook.

```tsx
import { MapContainer, TileLayer, Marker, Tooltip, useMapEvents } from "react-leaflet";

const AddMarker = () => {
  const [position, setPosition] = useState(null);

  useMapEvents({
    click: (e) => {
      setPosition(e.latlng); // 👈 add marker
      
      /* CODE TO ADD NEW PLACE TO STORE (check the source code) */
    },
  });

  return position === null ? null : (
    <Marker position={position}></Marker>
  );
};
const Map = ({places}) => {
  /* ... */

  return (
    <div className="map__container">
      <MapContainer
        center={defaultPosition}
        zoom={13}
      >
        {/* ... */}
        <AddMarker/>
        {/* ... */}
      </MapContainer>
    </div>
  );
};
```
As we visit Paris let's add Paris-Saint-Germain's stadium ⚽️  
So on click, we are showing the form to add a new place : 
![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/w4g2gxw6czj3vg9bozcq.png)

On save, the new place is added to our map 🎉.
![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/xunsjnxl9fk8mijj66q8.png)

### Draw lines ✏️

Now that we have learned how to add markers to our map, let's try to draw a line between each marker. 

For that, we are going to use [Polyline](https://react-leaflet.js.org/docs/api-components/#polyline) component.
This component needs a list of positions to draw the line. So let's create an array of positions with our places.

> I'll use useEffect to create this array only when places change.

```tsx
const Map = ({ places }) => {

  const [polyLineProps, setPolyLineProps] = useState([]);

  useEffect(() => {
      setPolyLineProps(places.reduce((prev: LatLngExpression[], curr: Place) => {
        prev.push(curr.position);
        return prev
        ;
      }, []))
    }, [places]);

  return (
    <MapContainer
          center={defaultPosition}
          zoom={13}
          scrollWheelZoom={false}
          style={{ height: "100vh" }}
          zoomControl={false}
        >
    {/* ... */}
      <Polyline positions={polyLineProps} /> {/* 👈 */}
    {/* ... */}

    </ MapContainer>
  )
}
```
![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/otswr9tpzljhc4u077gi.png)


### Conclusion
 In this article, we succeed to show a map and interact with it, like adding markers dynamically and drawing a line. To go further you can try to draw a [circle](https://react-leaflet.js.org/docs/api-components#circle) or a [rectangle](https://react-leaflet.js.org/docs/api-components#rectangle). 

Hope it helped some of you.  
Thanks for reading. 😇

> ### Source code available on [Github](https://github.com/Maj07/react-leaflet-example) 👈  

### Sources 
 - [React-Leaflet](https://react-leaflet.js.org/)
 - [Leaflet](https://leafletjs.com/)