maplibre-polygon-editor

Precise, topology-aware polygon editor for MapLibre GL, with a Vue 3 component. Shared borders stay shared; snapping helps but is never required.
1,028
Install
npm install maplibre-polygon-editor
Latest Version:0.2.0
Vue:3
License:MIT OR Apache-2.0
Last Updated:Oct 21, 2026
Links: GitHub  ·  npm
Maintainer: rsansores

maplibre-polygon-editor

A precise, topology-aware polygon editor for MapLibre GL JS, with a Vue 3 component on top. Draw delivery zones, sales territories, school districts — any set of areas that tile the ground — and keep their shared borders shared.

▶ Try it live — it runs entirely in your browser. The basemap is a single vector-tile file served next to the page: no tile server, no API key.

The editor: an area selected, its shared corner highlighted and its exact coordinates in the inspector

Why another polygon editor

Most map drawing tools treat every polygon on its own. Draw two areas side by side and you get two borders that almost touch: a sliver of gap here, a sliver of overlap there, and an address on the line that belongs to both or neither. This editor is built around the opposite rule:

  • A shared border is one border. Two areas that touch hold the same vertices along it. Drag a shared corner and every area holding it follows; insert a corner on a shared edge and it appears in both. A new area drawn over a neighbour is trimmed to the free ground and takes the neighbour's border vertex for vertex.
  • Every click lands exactly where it was made. No smoothing, no simplification, no vertex cap. Coordinates are rounded to a fixed precision (8 decimals ≈ 1 mm by default), which is what lets two areas hold the very same vertex.
  • The map data is a helper, never a gate. Corners snap to existing borders and to streets, and a segment between two clicks on the same border or street can follow it. But a border through a forest, across a field or through an unmapped neighbourhood is drawn just as easily: hold Alt, turn snapping off, type coordinates, or paste them. Nothing in the editor depends on the basemap being right.
  • Nothing is lost silently. An edit that would break a rule (a border crossing itself, two areas overlapping) is refused and explained; an unfinished drawing stays on screen to be fixed.

Features

Draw Click corners; click the first one, double-click or press Enter to close. Backspace removes the last corner.
Snap To corners, to borders (exactly onto the edge), to streets of the basemap. Hold Alt for a free point.
Trace Two clicks on the same border or street follow it, so a new area hugs its neighbour without clicking every vertex.
Follow roads Between two clicks on streets, the border follows the streets the map is drawing — in any direction, as close as possible to the straight line, never doubling back, crossing a river or a highway straight when the nearest bridge is far. No routing service needed.
Edit Drag corners (shared ones move together), drag a midpoint to add a corner, delete a corner, type exact coordinates.
Cut Draw a line across an area to split it in two; both halves share the cut.
Merge Join two areas that share a border.
Validate Same rules as PostGIS ST_IsValid, plus no overlaps (configurable: trim, forbid or allow; overlapping a locked area can be refused outright). Overlap is measured by thickness, so a rounding sliver along a shared border never counts.
Locked areas Shown, snapped to and checked against, but not editable — someone else's neighbouring areas.
Search Paste coordinates in any common format (decimal, degrees-minutes-seconds, geo: URIs), or plug in a geocoder (Photon adapter included).
Import / export GeoJSON (RFC 7946 winding) and KML (Google My Maps, Google Earth).
Undo / redo Every edit, as one step; a drag is one step.
Measure Area, perimeter, and the length of the segment being drawn.
Theming Inherits a shadcn-style design-token contract (--color-primary, …). Light and dark.
i18n English and Spanish built in; follows the host's vue-i18n locale with no wiring.

Install

npm i maplibre-polygon-editor maplibre-gl vue
<script setup lang="ts">
import { ref } from 'vue'
import { PolygonEditor, type Area } from 'maplibre-polygon-editor'
import 'maplibre-polygon-editor/styles.css'

const areas = ref<Area[]>([])
</script>

<template>
  <div style="height: 600px">
    <PolygonEditor v-model="areas" map-style="https://demotiles.maplibre.org/style.json" />
  </div>
</template>

areas is plain JSON — an array of { id, rings, properties }. Persist it however you like, or turn it into GeoJSON with toFeatureCollection(areas).

Using Vite with MapLibre 6? MapLibre loads its worker from a separate file; tell it where the bundler put it, once, at startup:

import { setWorkerUrl } from 'maplibre-gl'
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?url'
setWorkerUrl(workerUrl)

Documentation

  • Integration guide — props, your own layout, your own map, theming, i18n, snapping to your basemap's streets, search and routing services, self-hosting tiles, storing and validating areas on a server (PostGIS), and using the core without Vue.
  • How it works — the data model, how shared borders stay shared, the precision model, and how validation matches PostGIS.
  • Contributing and Releasing.

Packages and entry points

Import What it is
maplibre-polygon-editor The Vue 3 component (PolygonEditor), the headless composable (usePolygonEditor), the connected parts, and everything in /core.
maplibre-polygon-editor/core Framework-agnostic: the editor state machine (PolygonEditorCore), the MapLibre binding (MapBinding), geometry, GeoJSON/KML, adapters. No Vue.
maplibre-polygon-editor/styles.css The stylesheet for the Vue parts.

Peer dependencies: maplibre-gl 5 or 6, and vue 3.4+ (only for the Vue entry point).

Develop

pnpm install
pnpm dev                 # the demo, with hot reload, on http://localhost:5198
pnpm test                # unit tests (geometry, topology, editor flows)
pnpm test:e2e            # browser tests against the demo (Playwright + real MapLibre)
pnpm lint && pnpm type-check && pnpm format:check && pnpm knip
pnpm build               # the library, into dist/
pnpm build:demo          # the demo, into dist-demo/ (what GitHub Pages serves)

The demo's basemap (demo/public/data) is a 5.5 MB extract of one city — Querétaro, Mexico, with its rural edge — built by scripts/fetch-demo-data.sh from the Protomaps daily OpenStreetMap build.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option. Map data in the demo © OpenStreetMap contributors.

Related Packages

polygon-splitter

Split a polygon using a polyline.

512,281 53
shamos-hoey

A module to check if a polygon self-intersects

13,443 31
vue-map-radius

A Vue 3 component for radius and polygon visualization on MapTiler maps

1,707 0
icimg

A lightweight, dependency-free image editor with an Apple-inspired interface.

535
@codemirror/lang-vue

Vue template support for the CodeMirror code editor

71,371,417 29

Version History

Version Released Vue License
0.2.0 ^3.4.0 MIT OR Apache-2.0
0.1.0 ^3.4.0 MIT OR Apache-2.0
Pre-releases (1)
Version Released License
0.0.0-stage