PACKAGE
@element-editor/adapter-leaflet
Plug editing into an existing Leaflet or react-leaflet map. You control the map; Element controls the editing layer.
Screenshot: an existing Leaflet-based app layout (left sidebar, top toolbar from the host app, main content area). Inside the main area, a Leaflet map with other Leaflet plugins visible (a clustering layer, custom markers) AND Element's editing toolbar active on top. Makes clear the adapter doesn't replace — it adds.
Install
npm install @element-editor/adapter-leaflet @element-editor/ui leaflet react-leafletWith TypeScript, also npm install -D @types/leaflet.
Usage
import { MapContainer, TileLayer } from 'react-leaflet'
import { ElementEditorUIProvider, GeoStoreProvider, ElementEditorControls } from '@element-editor/adapter-leaflet'
import '@element-editor/adapter-leaflet/style.css' // includes Leaflet's CSS
export default function MyMap() {
return (
<ElementEditorUIProvider>
<GeoStoreProvider>
<div data-elm-theme="light">
<MapContainer center={[48.86, 2.35]} zoom={12} style={{ height: 600 }}>
<TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" />
<ElementEditorControls
onChange={(data) => console.log(data)}
/>
</MapContainer>
</div>
</GeoStoreProvider>
</ElementEditorUIProvider>
)
}ElementEditorUIProvider supplies the editor's contexts and its own toast <Toaster>; GeoStoreProvider holds the editor state and must wrap MapContainer. The data-elm-theme element scopes Element's styles (the stylesheet doesn't reset your page) and its keyboard shortcuts. ElementEditorControls must be inside MapContainer. The adapter attaches the editing layer to your existing map — no control is taken away, no event is hijacked.
To brand the editor, pass one colour: <ElementEditorUIProvider accentColor="#ea580c">. The UI and the map highlights follow it. Recolour the features with featureColor or per feature with getFeatureStyle. See Theming.
When to pick this package
Use @element-editor/adapter-leaflet when:
- You already have a Leaflet or react-leaflet map and want to add editing without replacing your map setup.
- You need custom layout — your toolbar lives in your app sidebar, your data panels in your own UI.
- You want to keep Leaflet's ecosystem — plugins, heatmaps, clusterers, custom layers — alongside the editing tools.
Use @element-editor/react instead if you want a drop-in that owns the map too (MapLibre by default). Want that drop-in on Leaflet? Import it from @element-editor/react/leaflet; it uses this adapter.
Key components
| Component | Purpose |
|---|---|
ElementEditorUIProvider | Context stack: error boundary, config, shortcuts, events and toasts, map controller. Wraps everything. |
GeoStoreProvider | Context provider. Wraps the map; stores all editor state. |
ElementEditorControls | The editing layer itself. Goes inside MapContainer. |
FeatureInteractionLayer | Draws the features and the selected-feature interactions (vertices, handles, overlays). Handled internally. |
DrawingLayer | Active drawing overlays. Handled internally. |
SnapController | Snapping logic. Enabled by default. |
You import only what you need. The adapter package re-exports ElementEditorUIProvider, GeoStoreProvider and the toolbars from @element-editor/ui; install @element-editor/ui yourself when you import its panels.
Bringing your own UI
By default, ElementEditorControls does not render a visible toolbar or panels — just the editing interaction layer on the map. You add UI by installing @element-editor/ui and placing components wherever your layout needs them:
import { Suspense } from 'react'
import { MapContainer, TileLayer } from 'react-leaflet'
import {
ElementEditorUIProvider,
GeoStoreProvider,
ElementEditorControls,
} from '@element-editor/adapter-leaflet'
import {
FloatingToolbar,
EditingToolbar,
LayersPanel,
DataTablePanel,
} from '@element-editor/ui'
<ElementEditorUIProvider>
<GeoStoreProvider>
<div data-elm-theme="light" style={{ position: 'relative', height: 600, overflow: 'hidden' }}>
<MapContainer center={[48.86, 2.35]} zoom={12} style={{ height: '100%' }}>
<TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" />
<ElementEditorControls />
</MapContainer>
{/* UI goes outside MapContainer, inside the data-elm-theme element */}
<FloatingToolbar />
<div style={{ position: 'absolute', bottom: 16, left: '50%', transform: 'translateX(-50%)', zIndex: 400, display: 'flex', gap: 8 }}>
<EditingToolbar />
</div>
<LayersPanel />
<Suspense fallback={null}><DataTablePanel /></Suspense>
</div>
</GeoStoreProvider>
</ElementEditorUIProvider>ElementEditorControls goes inside MapContainer; the panels go outside it, or Leaflet treats their clicks and scrolls as map gestures. The data table is lazy-loaded, so it sits in its own <Suspense>.
See the custom UI guide for the full component list.
Key props on ElementEditorControls
| Prop | Type | Description |
|---|---|---|
initialData | FeatureCollectionInput | null | Pre-populate with existing GeoJSON. |
onChange | (data: FeatureCollection) => void | Fires on every committed change. |
onFeatureAdd / onFeatureUpdate / onFeatureDelete | granular callbacks | Fires per-feature events. |
onSelectionChange | (ids: string[]) => void | Fires whenever selection changes. |
tools | DrawTool[] | Restrict the available drawing tools. |
schema | Schema | Pre-load a single schema. |
readonly | boolean | Block every user edit. The ref keeps working. |
onError | (error) => void | false | string | Every runtime failure, with a stable code and source. |
onReady | (map: L.Map) => void | The map, once Element's layers are on it. |
Full reference in the docs.
What's included
Everything in the Element feature set applies to this adapter:
- Drawing tools, vertex editing, selection & transform
- Snapping, boolean operations, geometry processing
- Measure, import and export
- Schemas, data table, GeoJSON viewer — via
@element-editor/ui
A note on migrating from Leaflet.Draw or Geoman
Both Leaflet.Draw and Leaflet-Geoman attach to L.Control. Element's adapter attaches via react-leaflet's component model instead. Coexistence during migration is possible — ElementEditorControls doesn't replace or hijack existing controls. See the Leaflet.Draw comparison and Geoman comparison for migration snippets.
Versions and dependencies
- React and React DOM
^19.2(React 18 is not supported) - Leaflet
^1.9(peer dep) - react-leaflet
^5(peer dep) @types/leafletfor TypeScript users@element-editor/coreand@element-editor/uicome in as regular dependencies; add them to your ownpackage.jsononly if you import from them (for example theFeatureCollectiontype from@element-editor/core). No bundler configuration is needed: the geometry worker is inlined.
Types ship in the package. TypeScript-first.