Skip to content

JS API ​

This page documents thirteen components — a single root component, MapboxMap, which owns the Mapbox Map instance, plus the twelve children below — built on the AbstractMapboxMapChild (and, for controls, AbstractMapboxControl) base classes. The StoreLocator orchestrator completes the family on its own page. Every map child resolves the closest parent MapboxMap on its own and registers itself against its map once it is loaded. MapboxClusterItem is the one exception: it is a rendered list element that extends Base directly, and resolves the closest MapboxCluster instead of a map.

Register each component you use — ideally through a lazy manifest entry so the heavy mapbox-gl dependency stays out of your main bundle (see Lazy loading).

Providing the mapbox-gl dependency ​

By default the components resolve mapbox-gl (and the optional @mapbox/mapbox-gl-geocoder) with a lazy import() the first time a map is built, so nothing needs configuring — keep mapbox-gl installed and it just works, staying out of your main bundle until a map is on the page.

To control which mapbox-gl the components use — a pinned version, a self-hosted build (for example a same-origin worker under a strict CSP), or a module served from an import map or a CDN — inject your own instance once, before the components mount:

js
import mapboxgl from 'mapbox-gl';
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
import { provideMapboxGl, provideMapboxGeocoder } from '@studiometa/ui-mapbox';

provideMapboxGl(mapboxgl);
provideMapboxGeocoder(MapboxGeocoder); // optional geocoder peer

Once provided, @studiometa/ui-mapbox never imports mapbox-gl by specifier — it uses the instance you handed it. resolveMapboxGl() and resolveMapboxGeocoder() are exported too if you want to trigger and await resolution yourself (for example to preload the module).

Reactivity and updates ​

To move the map, change its data or update a marker after mount, reach for the underlying Mapbox objects directly through the component instances:

js
import { getInstance } from '@studiometa/js-toolkit';

// The Mapbox `Map` instance is exposed on the `MapboxMap` component.
const mapboxMap = getInstance(element, MapboxMap);
mapboxMap.map.setCenter([2.35, 48.86]);
mapboxMap.map.setZoom(12);

// Markers, popups and sources expose their own Mapbox object too.
marker.marker.setLngLat([2.29, 48.86]);

Every component documents the Mapbox object it exposes (map, marker, popup, control, …) in its Getters section below.

Switching base style keeps your layers

Calling map.setStyle(…) wipes the entire style — every source, layer and sprite — while the map instance and the still-mounted child components survive. Each map child subscribes to the map's style.load and re-injects its contribution automatically, so your declarative sources, layers, images and clusters re-appear on the new base style instead of silently vanishing. You can switch the base style at runtime without re-declaring your data.

Map ​

MapboxMap ​

Display an interactive Mapbox GL map. This is the root component of the system and the parent of every other component. It instantiates the Mapbox Map lazily and re-emits its events.

Options ​

access-token ​
  • Type: String

Mapbox GL access token (required).

zoom ​
  • Type: Number

Initial zoom level.

center ​
  • Type: Array
  • Default: [0, 0]

Initial center as [longitude, latitude].

map-options ​
  • Type: Object
  • Default: {}

Any other Mapbox Map option, spread into the constructor. This is where style, pitch, bearing, bounds, … go.

The access-token, zoom and center options act as overridable defaults, then map-options is spread into the Mapbox Map constructor. The container is always resolved from the component (the container ref, falling back to the root element) and can not be overridden. Use map-options for anything the convenience options above do not cover — most notably the map style:

html
<div
  data-component="MapboxMap"
  data-option-access-token="pk.…"
  data-option-map-options='{ "style": "mapbox://styles/mapbox/streets-v12", "pitch": 45 }'>
  <div data-ref="container"></div>
</div>

Refs ​

container ​
  • Type: HTMLElement

The element used as the map container. Falls back to the root element when omitted.

Getters ​

map ​
  • Type: mapboxgl.Map | undefined

The underlying Mapbox GL map instance, or undefined until it has been built. The map is created in mounted(), after mapbox-gl resolves — it may be injected or lazily imported — so a caller should either wait for the map-load event or go through whenMapReady().

isLoaded ​
  • Type: boolean

Whether the map has finished loading (a public field, not a getter).

Events ​

The component emits a custom map-load event, plus all the common Mapbox GL map events prefixed with map- to avoid conflicts with native events. Every payload is a named object: map-load carries { map }, and every forwarded event carries the Mapbox event object as { event }.

map-load ​
  • Payload: { map }

The map finished loading (custom event).

map-idle ​

The map is idle after rendering.

map-render ​

A frame is rendered.

map-resize ​

The map container is resized.

map-remove ​

The map is removed.

map-error ​

An error occurred, carrying the Mapbox error event as { event }. Not to be confused with the map children's own map-error, which carries { error } — both bubble, so an ancestor listening for map-error should read the payload by component.

map-click ​

A click on the map.

map-dblclick ​

A double-click on the map.

map-mouseenter ​

The pointer enters the map canvas.

map-mouseleave ​

The pointer leaves the map canvas.

map-mousemove ​

The pointer moves over the map.

map-movestart ​

Map movement starts (pan, zoom, rotate).

map-move ​

The map is moving.

map-moveend ​

Map movement ends.

map-zoomstart ​

A zoom transition starts.

map-zoom ​

The zoom level changes.

map-zoomend ​

A zoom transition ends.

map-rotatestart ​

Rotation starts.

map-rotate ​

The map is rotating.

map-rotateend ​

Rotation ends.

map-pitchstart ​

A pitch transition starts.

map-pitch ​

The pitch changes.

map-pitchend ​

A pitch transition ends.

map-dragstart ​

A drag starts.

map-drag ​

The map is being dragged.

map-dragend ​

A drag ends.

Listen to these events from a parent component with on<ComponentName><EventName> methods:

js
import { Base, registerComponent } from '@studiometa/js-toolkit';
import { MapboxMap } from '@studiometa/ui-mapbox';

class App extends Base {
  static config = {
    name: 'App',
    components: { MapboxMap },
  };

  onMapboxMapMapLoad({ payload: { map } }) {
    console.log('Map is ready', map);
  }

  onMapboxMapMapClick({ payload: { event } }) {
    console.log('Clicked at', event.lngLat);
  }

  onMapboxMapMapZoomend({ payload: { event } }) {
    console.log('New zoom level', event.target.getZoom());
  }
}

registerComponent(App);

Markers & Popups ​

MapboxMarker ​

Add a marker to the map. A MapboxPopup nested inside the marker is automatically attached to it.

Options ​

lng-lat ​
  • Type: Array
  • Default: [0, 0]

Marker position as [longitude, latitude].

marker-options ​
  • Type: Object
  • Default: {}

Mapbox Marker options (color, anchor, draggable, …).

Getters ​

marker ​
  • Type: mapboxgl.Marker

The underlying Marker instance.

  • Type: MapboxPopup

The first nested MapboxPopup child, if any.

Methods ​

setChildPopup(popup) ​
  • Arguments: MapboxPopup

Attach a child MapboxPopup's popup to this marker. It is called automatically by whichever of the two mounts last — the marker pulls its child popup once the map is ready, and a popup that mounts after its marker (a dynamic append) pushes itself here. Setting the same popup twice is idempotent.

MapboxPopup ​

Display a popup on the map. It can be used standalone (placed directly on the map at a given position) or nested inside a MapboxMarker (attached to the marker, no lng-lat needed). The popup content is taken from the element's inner HTML.

Options ​

lng-lat ​
  • Type: Array
  • Default: [0, 0]

Popup position as [longitude, latitude] (standalone popups only).

  • Type: Object
  • Default: {}

Mapbox Popup options.

Getters ​

  • Type: mapboxgl.Popup

The underlying Popup instance.

Controls ​

All controls extend AbstractMapboxMapChild and expose the underlying Mapbox control through a control getter. They share a position option and are added to the map on mount, removed on unmount. MapboxGeocoder is the exception: it has no position option, and it can render in its own element, with or without a map.

MapboxNavigationControl ​

Add zoom in/out and compass controls to the map.

Options ​

position ​
  • Type: String
  • Default: 'top-right'

Control position: top-left, top-right, bottom-left or bottom-right.

show-compass ​
  • Type: Boolean
  • Default: false

Show the compass button.

show-zoom ​
  • Type: Boolean
  • Default: false

Show the zoom in/out buttons.

visualize-pitch ​
  • Type: Boolean
  • Default: false

Visualize the pitch on the compass button.

MapboxGeolocateControl ​

Add a button that uses the browser's geolocation API to locate the user on the map.

Options ​

position ​
  • Type: String
  • Default: 'top-right'

Control position.

position-options ​
  • Type: Object

Browser PositionOptions.

fit-bounds-options ​
  • Type: Object

FitBoundsOptions used when tracking.

track-user-location ​
  • Type: Boolean
  • Default: false

Continuously track the user location.

show-accuracy-circle ​
  • Type: Boolean
  • Default: false

Show the accuracy circle around the user location.

show-user-location ​
  • Type: Boolean
  • Default: false

Show the user location dot.

show-user-heading ​
  • Type: Boolean
  • Default: false

Show the user heading indicator.

MapboxFullscreenControl ​

Add a button that toggles the map fullscreen.

Options ​

position ​
  • Type: String
  • Default: 'top-right'

Control position.

MapboxGeocoder ​

Add an address search control powered by @mapbox/mapbox-gl-geocoder. Install the optional @mapbox/mapbox-gl-geocoder peer dependency to use it.

Optional, loaded on demand

@mapbox/mapbox-gl-geocoder is an optional peer dependency. It is loaded lazily with a dynamic import() when a MapboxGeocoder mounts, so the rest of the package works without it installed. If you use this component, add it to your project: npm install @mapbox/mapbox-gl-geocoder.

With autoloading, MapboxGeocoder uses the visible mount strategy, so it mounts and loads the library when its element nears the viewport. Do not put hidden on its element: a hidden element never does. Use data-mount="eager" to load it without waiting for the viewport.

Options ​

access-token ​
  • Type: String

Mapbox access token for the geocoding requests. Inside a MapboxMap, the geocoder uses the map's access-token when this option is not set. Without a parent map, the geocoder needs it (see Standalone usage).

The token is taken from the first of these that is set:

  1. accessToken in the options option.
  2. The access-token option.
  3. The access-token of the parent MapboxMap.

This is the same order as on MapboxMap, where an accessToken in map-options wins over its access-token option.

add-to-map ​
  • Type: Boolean
  • Default: false

Add the geocoder to the map as a control. This requires a parent MapboxMap. Otherwise it is rendered inside the component's element, with or without a parent map (see Standalone usage).

options ​
  • Type: Object
  • Default: {}

Geocoder options. Non-serializable options (filter, externalGeocoder, render, getItemValue, localGeocoder) are not supported.

An accessToken set in options wins over the geocoder's access-token option. Prefer the access-token option: it keeps the token out of the JSON.

Getters ​

control ​
  • Type: MapboxGeocoderControl | undefined

The underlying mapbox-gl-geocoder control instance. It is created once the lazily imported geocoder module resolves, so it is undefined until then.

target ​
  • Type: Map | HTMLElement | string

Where the control is mounted, depending on add-to-map.

Events ​

map-result ​
  • Payload: { result }

Emitted when the geocoder resolves an address, carrying the geocoder's selected feature.

Standalone usage ​

The geocoder does not need a map. Without add-to-map and without an ancestor element declared as MapboxMap, the component works on its own: it creates the control on mount, adds the search input to its own root element and emits map-result with the selected feature. Use it for an address search that drives something other than a map, such as a search form.

A standalone geocoder has no map to read the access token from, so set its access-token option. Without a token, the component logs a mapbox-geocoder.missing-access-token warning and does not create the control. A standalone geocoder does not load mapbox-gl.

The following example fills a hidden near field with the coordinates of the selected address, then submits a Fetch form. An Action on the same element listens to map-result. The field stays disabled until an address is selected, so the form never sends an empty near parameter.

html
<form id="store-search" action="/stores" method="get" data-component="Fetch">
  <input type="hidden" name="near" disabled />
</form>

<div
  data-component="Action MapboxGeocoder"
  data-option-access-token="pk.…"
  data-option-options='{"countries": "fr"}'
  data-on:map-result="Fetch(#store-search) -> () => {
    const [lng, lat] = event.detail.result.center;
    const { near } = target.$el.elements;
    near.value = `${lat},${lng}`;
    near.disabled = false;
    target.$el.requestSubmit();
  }"></div>

<div id="stores">…</div>

Inside a MapboxMap, the geocoder uses the map, even when the map mounts after it: the access token falls back to the map's access-token. Only a geocoder added to the map with add-to-map shows a marker for the result and moves the map: a geocoder rendered in its own element never does, with or without a parent map.

In a StoreLocator, the geocoder can also be placed outside the MapboxMap element, anywhere inside the StoreLocator element. It then works standalone and needs its own access-token, and the StoreLocator still frames the map on its map-result event.

Data ​

MapboxSource ​

Add a source to the map. On unmount, every layer tied to the source is removed before the source itself.

Options ​

id ​
  • Type: String

Unique source id, referenced by layers.

source ​
  • Type: Object

A source specification, e.g. { "type": "geojson", "data": … }.

Refs ​

geojson ​
  • Type: HTMLScriptElement

Optional <script data-ref="geojson" type="application/json"> element holding inline GeoJSON. When present, its parsed content is injected as the source spec's data. Invalid JSON is ignored with a warning.

Getters ​

source ​
  • Type: SourceSpecification

The resolved source specification, with the geojson ref's inline data injected as data when present.

MapboxLayer ​

Add a layer to the map.

Options ​

id ​
  • Type: String

Unique layer id. It is assigned to the layer specification on mount.

layer ​
  • Type: Object

A layer specification.

before-id ​
  • Type: String

Insert the layer before this existing layer id.

MapboxImage ​

Load a single image and register it against the map sprite so it can be referenced from a symbol layer's icon-image.

Options ​

name ​
  • Type: String

The id under which the image is registered in the sprite.

url ​
  • Type: String

The URL of the image (png, webp or jpg).

options ​
  • Type: Object
  • Default: { pixelRatio: 1, sdf: false }

Options forwarded to map.addImage.

Events ​

map-ready ​
  • Payload: { name, image, options }

Emitted once the image has been loaded and added to the sprite.

MapboxImages ​

Load and register a list of images against the map sprite in one component.

Options ​

sources ​
  • Type: Array
  • Default: []

A list of image definitions, each { name, url, options? }. See MapboxImage options.

Events ​

map-ready ​
  • Payload: { images }

Emitted once every image has been loaded and added, carrying the loaded images.

Cluster ​

MapboxCluster ​

A clustered GeoJSON source driver whose features ARE its rendered items. The MapboxClusterItems living in its subtree self-register, and the cluster derives its clustered GeoJSON source from that registry — the same markup drives both a sidebar list and the clustered points on the map. It sets up the clustered source, the cluster circles layer, the cluster count labels layer and the unclustered points layer, together with the click-to-zoom interaction on clusters and pointer feedback on features.

The cluster is deliberately thin: it owns only the map data and the clustering interaction. It does not select, fly to, filter by viewport or open popups — those search-UX concerns belong to the optional StoreLocator orchestrator, which drives them on top of a cluster. Used on its own, a MapboxCluster still renders a working clustered map + list; it simply reports a click on an unclustered point through the map-item-click event and lets the caller decide what it means.

Options ​

cluster-max-zoom ​
  • Type: Number
  • Default: 14

Max zoom at which points are clustered.

cluster-radius ​
  • Type: Number
  • Default: 50

Radius of each cluster when clustering points.

cluster-min-points ​
  • Type: Number
  • Default: 2

Minimum number of points to form a cluster.

cluster-properties ​
  • Type: Object
  • Default: {}

Custom cluster properties.

clusters-layout ​
  • Type: Object
  • Default: {}

Layout for the clusters circle layer.

clusters-paint ​
  • Type: Object
  • Default: { 'circle-color': '#000', 'circle-radius': 40 }

Paint for the clusters circle layer.

cluster-count-layout ​
  • Type: Object
  • Default: { 'text-field': ['get', 'point_count_abbreviated'] }

Layout for the cluster count labels.

cluster-count-paint ​
  • Type: Object
  • Default: { 'text-color': 'white' }

Paint for the cluster count labels.

unclustered-point-layer-type ​
  • Type: String
  • Default: 'circle'

Type of the unclustered points layer.

unclustered-point-layout ​
  • Type: Object
  • Default: {}

Layout for the unclustered points layer.

unclustered-point-paint ​
  • Type: Object
  • Default: { 'circle-color': '#000', 'circle-radius': 4 }

Paint for the unclustered points layer.

Getters ​

items ​
  • Type: MapboxClusterItem[]

The registered items, in registration order — the read-only surface for an orchestrator.

featureCollection ​
  • Type: FeatureCollection

The GeoJSON derived from the registered items — the data pushed to the source.

Methods ​

register(item) ​

Register a MapboxClusterItem and schedule a coalesced rebuild. Called automatically by items on mount.

unregister(item) ​

Unregister a MapboxClusterItem and schedule a coalesced rebuild. Called automatically by items on unmount.

setData(data) ​

Replace the live source data directly, bypassing the item registry — for imperative control. Safe before mount or after teardown.

Events ​

map-cluster-click ​
  • Payload: { clusterId, event }

A cluster was clicked. Call event.preventDefault() to skip the default zoom-to-cluster behavior.

map-item-click ​
  • Payload: { item, feature, event }

An unclustered point was clicked. item is the registered MapboxClusterItem behind the feature, or undefined when none could be resolved.

map-update ​
  • Payload: { items }

The item set changed (a rebuild ran). Carries the live item set so an orchestrator can re-fit and re-filter.

MapboxClusterItem ​

A single entry of a MapboxCluster — at once a rendered list item AND a map feature. It resolves the closest MapboxCluster ancestor, pushes itself into its registry on mount (register) and pulls itself out on unmount (unregister), so the cluster never has to query for its children. It is headless and passive: it never selects itself — a StoreLocator orchestrator (when one wraps the cluster) drives its state setters.

Options ​

id ​
  • Type: String

Stable identifier, used to match a clicked map feature back to the item.

lng-lat ​
  • Type: Array
  • Default: [0, 0]

The point coordinates as [longitude, latitude].

properties ​
  • Type: Object
  • Default: {}

Extra feature properties merged into the item's GeoJSON feature.

Markup ​

[data-ref="popup"] ​
  • Type: HTMLElement

Optional descendant. It is resolved with a plain query rather than declared as a js-toolkit ref, so it works at any depth: its inner HTML is used as the popup content on selection; otherwise the item's whole inner HTML is used as a fallback.

Getters ​

id ​
  • Type: string

The item's stable identifier.

lngLat ​
  • Type: [number, number]

The item's [lng, lat] coordinates.

properties ​
  • Type: Record<string, unknown>

The extra feature properties.

popupContent ​
  • Type: string

The HTML used as the popup content when selected.

Methods ​

The state setters below are called by a StoreLocator orchestrator; you generally read the resulting data-attributes from CSS rather than calling them yourself.

setInBounds(value) ​

Toggle the data-in-bounds attribute — the list-visibility signal.

setActive(value) ​

Toggle the data-active attribute and the aria-current="true" state — the selected signal.

Events ​

map-error ​
  • Payload: { error }

Emitted when unregistering the item throws. The failure is contained and reported on the diagnostic channel as well.

AbstractMapboxMapChild ​

The base class every child component extends (controls extend AbstractMapboxControl, itself a thin subclass that adds the shared position option and the map.addControl/removeControl lifecycle). It resolves the closest parent MapboxMap and exposes its Mapbox Map instance so children can register themselves against it. Beyond mapboxMap/map, it hardens the whole family's lifecycle: guarded injection and teardown, dead-map safety, retryable parent resolution (a child mounted before its map re-resolves on mapbox-map:connected), and automatic re-injection after a map.setStyle(…). Extend it to build your own map children.

Getters ​

mapboxMap ​
  • Type: MapboxMap

The closest parent MapboxMap component instance.

map ​
  • Type: mapboxgl.Map | undefined

The Mapbox Map instance of the parent map, or undefined while the parent is still resolving mapbox-gl. Use whenMapReady() rather than reading it directly in mounted().

Methods ​

whenMapReady(callback) ​
  • Arguments: (map: mapboxgl.Map) => void | Promise<void>

Run the callback once the parent map is ready — now, on the next map-load, or once a MapboxMap connects if none exists yet. This is the entry point every child uses. The callback is stored so it can be re-run against a fresh map after a destroy and remount, it never fires after the child is destroyed, and a throw — or a rejected promise, for an async callback — is contained rather than rethrown, reported through $error() and emitted as a map-error event. An async callback must re-check that the map it was handed is still the current one after every await, since the map can be removed or replaced while the promise is in flight.

Events ​

map-error ​
  • Payload: { error }

Emitted when guarded map injection or teardown throws. The failure is contained and reported on the diagnostic channel as well.

js
import { AbstractMapboxMapChild } from '@studiometa/ui-mapbox';

export class MyMapChild extends AbstractMapboxMapChild {
  static config = {
    name: 'MyMapChild',
  };

  mounted() {
    this.whenMapReady((map) => {
      // `map` is the fully loaded Mapbox `Map` instance.
      map.addLayer(/* … */);
    });
  }
}

Document-level connection events ​

Two constants are exported for integrators building their own children:

  • MAPBOX_MAP_CONNECTED ('mapbox-map:connected') — dispatched on document by a MapboxMap once its Mapbox instance exists, with the component instance in detail. A child that mounted before its map uses it to retry resolving its parent.
  • MAPBOX_CLUSTER_CONNECTED ('mapbox-cluster:connected') — the same handshake between a MapboxCluster and a MapboxClusterItem that mounted first.

AbstractMapboxControl ​

The base class for controls registered through map.addControl. It extends AbstractMapboxMapChild, creates the underlying Mapbox control lazily, and handles its add/remove lifecycle. Extend it when implementing a custom control component.

Options ​

position ​

  • Type: ControlPosition
  • Default: 'top-right'

The corner where Mapbox adds the control.

Getters ​

control ​

  • Type: IControl

The lazily created Mapbox control instance.

Methods ​

createControl(options) ​

Create and return the concrete Mapbox control. Subclasses must implement this method.

js
import { AbstractMapboxControl } from '@studiometa/ui-mapbox';
import { NavigationControl } from 'mapbox-gl';

export class CompactNavigationControl extends AbstractMapboxControl {
  static config = {
    name: 'CompactNavigationControl',
  };

  createControl(options) {
    return new NavigationControl({ ...options, showCompass: false });
  }
}