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/sdk

The 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, TOPOGRAPHIC variants 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 ready state above does, after load.
  • MapTiler CSS import. @maptiler/sdk/dist/maptiler-sdk.css — and it's a separate CSS file from Mapbox's, so don't accidentally import mapbox-gl.css.
  • API key handling. Don't commit keys. Use environment variables; expose only the client-safe key if MapTiler provides read-only variants.

Ready to ship?