How to create a React app using Vite, TypeScript, and Geoman

Vite is a recent entrant to the toolchain for building JavaScript and TypeScript frontend apps. It has gained a lot of popularity (as witnessed by its >64 thousand stars on GitHub and 12 million weekly downloads on npmjs) for its speed versus the traditional Create React App which is heavy on dependencies. Vite is a great tool for creating a React app using TypeScript and Geoman. We will show you how to get started by setting up the scaffolding. If you do not want to follow along, you can jump straight to our GitHub repo for the code.

Create a Vite project

Vite ships natively with the JavaScript package managers like NPM, Yarn, Bun, and PNPM. Using NPM, you can set up a Vite project by opening your terminal and running:

npm create vite@latest

This will start a CLI to guide you through selecting a Project Name, your JavaScript Framework, your Variant. In this example we pick React as the framework and TypeScript as the Variant.

Vite CLI

Install Leaflet and Leaflet-Geoman

Now that we have set up our Vite 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 these, we are ready to implement the business logic for our app. We will be creating two components, one for listening to events and one for creating the Geoman controls.

React Components for Events Listeners and Geoman Controls

We create a file src/components/Events.tsx for the Event Listener component. This component integrates with our map using React Leaflet and adds various event listeners to handle different interactions like creation, modification, and removal 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;

Next, we setup 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);

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 Events from "./components/Events";
import { GeomanControl } from "./components/GeomanControl";
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.

index.html

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vite + React + TS</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>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>

With these edits we now have a React app created by Vite that displays a Leaflet map with the Geoman controls. Runnning npm run dev:

Geoman in a Vite Project

GitHub repo with full code and more examples

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

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

Ready to dive in?
Start with Leaflet-Geoman Today.