Using create-react-app with TypeScript and Geoman
Apr 17, 2024
Using create-react-app with TypeScript and Geoman
Niclas Priess
Niclas Priess
Head of Sales

The traditional way of setting up a React app is to use create-react-app. React is a great frontend Framework for using Geoman and Leaflet and we will show you how to get started by setting up the scaffolding. If you just want to dive in, you can jump straight to our GitHub repo for the code.

Use create-react-app to set up the project

You can use create-react-app with the JavaScript package managers like NPX, NPM and Yarn. Here we use NPX and set up a React app project by opening a terminal and running:

npx create-react-app geoman-create-react-app --template typescript

This will create the initial scaffolding for our React app. The --template typescript flag insures that we use TypeScript in our app.

Install Leaflet and Leaflet-Geoman

Now that we have set up our React app project, we need to install Leaflet and Geoman.

npm i @geoman-io/leaflet-geoman-free leaflet react-leaflet
npm install -D @types/leaflet

After installing the libraries, we can implement the business logic for our app. We will create two components, one for adding the Geoman controls to our map and one for listening to events.

React Components for Geoman Controls and Events Listeners

We start by setting up the Geoman Control component which adds the Geoman functionality to our Leaflet map.

src/components/GeomanControl.tsx

import { createControlComponent } from "@react-leaflet/core";
import * as L from "leaflet";
import "@geoman-io/leaflet-geoman-free";
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css";

interface Props extends L.ControlOptions {
  position: L.ControlPosition;
  drawCircle?: boolean;
  oneBlock?: boolean;
}

const Geoman = L.Control.extend({
  options: {},
  initialize(options: Props) {
    L.setOptions(this, options);
  },

  addTo(map: L.Map) {
    if (!map.pm) return;

    map.pm.addControls({
      ...this.options,
    });
  },
});

const createGeomanInstance = (props: Props) => {
  return new Geoman(props);
};

export const GeomanControl = createControlComponent(createGeomanInstance);

Next, we need the Event Listener component and create a file src/components/Events.tsx. This component integrates with our Leaflet map using React Leaflet and adds various event listeners to handle different interactions like creation and editing of map layers.

src/components/Events.tsx

import { useEffect } from "react";
import { useMap } from "react-leaflet";

const Events = () => {
  const map = useMap();

  useEffect(() => {
    if (map) {
      map.on("pm:create", (e) => {
        console.log("Layer created:", e);

        e.layer.on("click", () => {
          console.log("Layer clicked", e);
        });

        e.layer.on("pm:edit", () => {
          console.log("Layer edited", e);
        });

        e.layer.on("pm:update", () => {
          console.log("Layer updated", e);
        });

        e.layer.on("pm:remove", (e) => {
          console.log("Layer removed:", e);
        });

        e.layer.on("pm:dragstart", (e) => {
          console.log("Layer dragstart:", e);
        });

        e.layer.on("pm:dragend", (e) => {
          console.log("Layer dragend:", e);
        });
      });

      map.on("pm:drawstart", (e) => {
        console.log("Layer drawstart:", e);
      });

      map.on("pm:drawend", (e) => {
        console.log("Layer drawend:", e);
      });

      map.on("pm:globaldrawmodetoggled", (e) => {
        console.log("Layer globaldrawmodetoggled:", e);
      });

      map.on("pm:globaldragmodetoggled", (e) => {
        console.log("Layer globaldragmodetoggled:", e);
      });

      map.on("pm:globalremovalmodetoggled", (e) => {
        console.log("Layer globalremovalmodetoggled:", e);
      });

      map.on("pm:globalcutmodetoggled", (e) => {
        console.log("Layer globalcutmodetoggled:", e);
      });

      map.on("pm:globalrotatemodetoggled", (e) => {
        console.log("Layer globalrotatemodetoggled:", e);
      });
    }
  }, [map]);

  return null;
};

export default Events;

We can now integrate these two components in our React app.

Integrate the React Components

We rewrite the App.tsx logic to include our components and to display a Leaflet map.

src/App.tsx

import { MapContainer, TileLayer } from "react-leaflet";
import { GeomanControl } from "./component/GeomanControl";
import Events from "./component/Events";

const App = () => {
  return (
    <MapContainer
      center={[51.505, -0.09]}
      zoom={13}
      scrollWheelZoom={true}
      style={{ width: "100vw", height: "100vh" }}
    >
      <TileLayer
        attribution='<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>'
        url="https://api.maptiler.com/maps/streets-v2/256/{z}/{x}/{y}.png?key=FTejVijNkqKWPbQui8i9"
      />
      <GeomanControl position="topleft" oneBlock />
      <Events />
    </MapContainer>
  );
};

export default App;

We need to include the Leaflet and Geoman CSS in our HTML head tag in order for the map to display properly.

public/index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>React App</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
      integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
      crossorigin=""
    />
    <link
      rel="stylesheet"
      href="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.css"
    />

    <script
      src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
      integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
      crossorigin=""
    ></script>
    <script src="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.js"></script>
  </head>

  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

With these edits we now have used create-react-app to create a React app that displays a Leaflet map with the Geoman controls. Running npm start spins up a server:

Geoman in a create-react-app project

GitHub repo with full code and more examples

You can visit GitHub repo to see the full code and other starter kits for TypeScript Apps with Geoman.

If you have an interesting example of how you have used Geoman and React, we invite you to share it on our GitHub Repo.


Keywords