PACKAGE
@element-editor/adapter-maplibre
Plug editing into MapLibre GL JS, MapTiler SDK, or Mapbox GL JS. One adapter covers them all — they share API lineage.
Screenshot: a MapLibre GL JS vector-tile map (styled streets, building outlines visible) with Element's drawing toolbar active. A freshly drawn polygon is overlaid. The engine-style tiles and Element's editing layer are clearly coexisting.
Install
npm install @element-editor/adapter-maplibre @element-editor/ui maplibre-glFor Mapbox GL JS or MapTiler SDK, install that package as well. Keep maplibre-gl: the adapter imports it itself.
Usage
import { useEffect, useRef, useState } from 'react'
import maplibregl from 'maplibre-gl'
import 'maplibre-gl/dist/maplibre-gl.css'
import { ElementEditorUIProvider, GeoStoreProvider, ElementEditorControls } from '@element-editor/adapter-maplibre'
import '@element-editor/adapter-maplibre/style.css'
export default function MyMap() {
const container = useRef<HTMLDivElement>(null)
const [map, setMap] = useState<maplibregl.Map | null>(null)
useEffect(() => {
const m = new maplibregl.Map({
container: container.current!,
style: 'https://demotiles.maplibre.org/style.json',
center: [2.35, 48.86],
zoom: 12,
})
setMap(m)
return () => { m.remove(); setMap(null) }
}, [])
return (
<ElementEditorUIProvider>
<GeoStoreProvider>
<div data-elm-theme="light" style={{ position: 'relative', height: 600 }}>
<div ref={container} style={{ width: '100%', height: '100%' }} />
{map && (
<ElementEditorControls
map={map}
onChange={(data) => console.log(data)}
/>
)}
</div>
</GeoStoreProvider>
</ElementEditorUIProvider>
)
}ElementEditorUIProvider supplies the editor's contexts and its own toast <Toaster>. The data-elm-theme element scopes Element's styles and keyboard shortcuts. ElementEditorControls waits for the style to accept sources, so it can mount as soon as the map exists.
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.
Pass the map instance to ElementEditorControls via the map prop, and keep it in React state so the component renders once the map exists. That's the one extra step over the Leaflet adapter, where ElementEditorControls finds the map through react-leaflet's context.
When to pick this package
Use @element-editor/adapter-maplibre when:
- Your app is on MapLibre GL JS — the open-source fork of Mapbox GL JS.
- Your app is on MapTiler SDK — MapTiler's wrapper around the same engine family.
- Your app is on Mapbox GL JS v3+ — the commercial engine.
They share enough API surface that one adapter covers them all. The map prop accepts any of them.
Use @element-editor/react for a drop-in MapLibre editor that owns its map, or @element-editor/adapter-leaflet for Leaflet-specific plug-in.
The map-load timing pattern
Vector tile engines need their style to finish loading before layers can be added. You don't have to time this yourself: ElementEditorControls waits until the style can accept sources (it retries on styledata and load) and puts its layers back after every map.setStyle(). Waiting for load before you hand over the map is still the safe pattern:
const [map, setMap] = useState<maplibregl.Map | null>(null)
useEffect(() => {
const m = new maplibregl.Map({ /* ... */ })
m.on('load', () => setMap(m))
return () => { setMap(null); m.remove() }
}, [])
{map && <ElementEditorControls map={map} />}A map held only in a useRef doesn't re-render your component, so keep it in state.
Key components and props
Same as @element-editor/adapter-leaflet, with one addition:
| Prop | Type | Description |
|---|---|---|
map | maplibregl.Map | The map instance. Required. A MapTiler SDK map works as is; a Mapbox GL JS map needs a TypeScript cast (map as any). |
The other props (onChange, onFeatureAdd, tools, schema, readonly, etc.) match the Leaflet adapter, with three differences: maxBounds instead of bounds, a children prop for @element-editor/ui components, and no renderer. See the full reference.
Also exports a useMapLibre hook for components deeper in the tree that need the map instance without prop drilling.
Basemaps
@element-editor/adapter-maplibre/basemaps is the same built-in catalog the drop-in editor uses: streets, terrain, light, dark (vector, OpenFreeMap) and osm (raster), plus 'auto', which follows the editor theme. No API key.
import { loadBasemapStyle, setBasemap } from '@element-editor/adapter-maplibre/basemaps'
const map = new maplibregl.Map({ container, style: await loadBasemapStyle('auto', { theme: 'light' }) })
await setBasemap(map, 'terrain') // switches style and keeps Element's layersRender BasemapControl (from the main entry) for a ready-made picker. See the basemaps guide and map styles.
Mapbox GL JS compatibility
Mapbox GL JS v3+ is API-compatible with MapLibre GL JS at runtime for the operations Element relies on. The same adapter works for either; TypeScript needs a cast, because the two packages' type names differ:
import mapboxgl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css'
import { GeoStoreProvider, ElementEditorControls } from '@element-editor/adapter-maplibre'
mapboxgl.accessToken = 'YOUR_TOKEN'
const map = new mapboxgl.Map({
container: /* ... */,
style: 'mapbox://styles/mapbox/streets-v12',
})
// Same pattern: wait for load, then <ElementEditorControls map={map as any} />MapTiler SDK compatibility
MapTiler SDK also works:
import { Map as MapTilerMap } from '@maptiler/sdk'
import '@maptiler/sdk/dist/maptiler-sdk.css'
import { GeoStoreProvider, ElementEditorControls } from '@element-editor/adapter-maplibre'
const map = new MapTilerMap({
container: /* ... */,
apiKey: 'YOUR_KEY',
})What's included
Same feature set as the Leaflet 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
Versions and dependencies
- React and React DOM
^19.2(React 18 is not supported) - maplibre-gl
^5.5(peer); Mapbox GL JS and MapTiler SDK instances also work at runtime @element-editor/coreand@element-editor/uicome in as regular dependencies; no bundler configuration is needed (the geometry worker is inlined).DrawingCursorHintis no longer exported.
Types ship in the package.
Ready to plug it in?
Open the playground · Full API reference · vs Mapbox GL Draw