INTEGRATION
Element Editor with MapTiler SDK
MapTiler SDK is an enriched MapLibre GL JS. Element's adapter-maplibre package works with all three — MapTiler, MapLibre, and Mapbox GL JS.
Install
npm install @element-editor/adapter-maplibre @element-editor/ui maplibre-gl @maptiler/sdkThe adapter imports maplibre-gl itself, so keep it installed next to the SDK.
Usage
import { useEffect, useRef, useState } from 'react'
import { Map as MapTilerMap, MapStyle } from '@maptiler/sdk'
import '@maptiler/sdk/dist/maptiler-sdk.css'
import { ElementEditorUIProvider, GeoStoreProvider, ElementEditorControls } from '@element-editor/adapter-maplibre'
import '@element-editor/adapter-maplibre/style.css'
const MAPTILER_KEY = process.env.NEXT_PUBLIC_MAPTILER_KEY!
export default function MapEditor() {
const container = useRef<HTMLDivElement>(null)
const mapRef = useRef<MapTilerMap | null>(null)
const [ready, setReady] = useState(false)
useEffect(() => {
const map = new MapTilerMap({
container: container.current!,
style: MapStyle.STREETS,
center: [2.35, 48.86],
zoom: 12,
apiKey: MAPTILER_KEY,
})
mapRef.current = map
map.on('load', () => setReady(true))
return () => {
map.remove()
}
}, [])
return (
<ElementEditorUIProvider>
<GeoStoreProvider>
<div data-elm-theme="light" style={{ position: 'relative', height: 600 }}>
<div ref={container} style={{ width: '100%', height: '100%' }} />
{ready && mapRef.current && (
<ElementEditorControls
map={mapRef.current}
onChange={(data) => console.log(data)}
/>
)}
</div>
</GeoStoreProvider>
</ElementEditorUIProvider>
)
}Pass the MapTilerMap instance to ElementEditorControls via the map prop, as is (no cast: MapTiler SDK extends maplibregl.Map). Waiting for the map's load event is the safe pattern; Element also waits by itself until the style can accept its layers. ElementEditorUIProvider, GeoStoreProvider and the data-elm-theme element are required, as with every adapter.
Why the adapter works for both MapTiler and MapLibre
MapTiler SDK is a thin layer over MapLibre GL JS — it adds API helpers, styles, and geocoding, but the underlying map surface is identical. adapter-maplibre calls the shared map methods (addSource, addLayer, on('mousemove'), project, unproject, etc.) that both expose the same way.
MapTiler-specific features that pair well
- Vector tiles with built-in styles. The
MapStyle.STREETS,SATELLITE,TOPOGRAPHICvariants all work under the editor — Element's layers sit on top of whatever tiles you pick. - Geocoding. MapTiler's geocoder plugin coexists with Element — the search result centres the map, then the user draws.
- 3D terrain. Element's 2D editor sits on top of terrain-enabled maps without special handling, though vertex-drag feel varies with terrain exaggeration.
Known gotchas
- Re-render once the map exists. A map held only in a ref doesn't re-render your component; the
readystate above does, afterload. - MapTiler CSS import.
@maptiler/sdk/dist/maptiler-sdk.css— and it's a separate CSS file from Mapbox's, so don't accidentally importmapbox-gl.css. - API key handling. Don't commit keys. Use environment variables; expose only the client-safe key if MapTiler provides read-only variants.