This is the documentation of version 0.57.0. Go to the latest version, 1.0.0-rc.5.

Module lifecycle events

Module lifecycle events let you react to internal state changes within a map module — independent of any user interaction. Two event types are available:

Both use the same subscription model: events.on(type, handler) returns an unsubscribe function.

config-change

Every module emits config-change when its configuration is updated, regardless of how the change was triggered. This is useful for keeping external UI in sync with module state without coupling UI logic to individual setters.

import { TrafficFlowModule } from '@tomtom-org/maps-sdk/map';

const trafficFlow = await TrafficFlowModule.get(map, { visible: false });

const unsub = trafficFlow.events.on('config-change', (config) => {
    console.log('TrafficFlow config updated:', config);
    // config reflects the module's full current configuration
});

// Each of these triggers a config-change event:
trafficFlow.setVisible(true);
trafficFlow.applyConfig({ visible: false });

What triggers config-change

Any method that mutates the module's configuration fires the event. Common examples:

Method Module
setVisible(visible) All modules
applyConfig(config) All modules
updateConfig(partial) All modules
resetConfig() All modules
applyTheme(theme) PlacesModule
moveBeforeLayer(layer) GeometriesModule, PlacesModule
setMode(mode) TrafficAreaAnalyticsModule
setMetric(metric) TrafficAreaAnalyticsModule
filterCategories(filter) POIsModule
setLayerGroupsVisibility(...) BaseMapModule

The received config argument reflects the module's complete current configuration after the change.

Practical example: syncing a toggle button

import { TerrainModule } from '@tomtom-org/maps-sdk/map';

const terrain = await TerrainModule.get(map, { hillshade: false });
const toggle = document.querySelector('#hillshade-toggle') as HTMLInputElement;

terrain.events.on('config-change', (config) => {
    // Keep the toggle in sync regardless of who changed visibility
    toggle.checked = config?.hillshade ?? false;
});

toggle.addEventListener('change', () => terrain.setHillshadeVisible(toggle.checked));

shown-features

The shown-features event fires immediately after a module renders new data on the map. It is only available on modules that have an explicit show method.

import { PlacesModule } from '@tomtom-org/maps-sdk/map';
import { search } from '@tomtom-org/maps-sdk/services';

const places = await PlacesModule.create(map);

const unsub = places.events.on('shown-features', (features) => {
    // A module reports every one of its show operations here, and the payload says which ran.
    if ('places' in features) {
        updateResultsPanel(features.places);
    } else {
        updateConnectionsPanel(features.connections);
    }
});

const results = await search({ query: 'coffee' });
await places.show(results);        // { places: … }
await places.showConnections([]);  // { connections: … }

Which modules support shown-features

A module emits one shown-features stream covering all of its show operations, and the payload says which one ran:

Module Triggered by Feature type
PlacesModule show(places) / showConnections(connections) { places } or { connections }
GeometriesModule show(features) PolygonFeatures
RoutingModule showRoutes(routes) / showWaypoints(waypoints) { routes } or { waypoints }
CustomGeoJSONModule show(data, sourceName) { sourceName, data }
TrafficIncidentOverlayModule show(result) TrafficIncidentDetails
TrafficAreaAnalyticsModule show(data) TrafficAreaAnalytics

Modules that only control existing map data — TrafficFlowModule, TrafficIncidentsModule, TerrainModule, BaseMapModule, POIsModule — have no show, and events.on('shown-features', …) does not compile on them.

Practical example: fit map to shown results

import { GeometriesModule } from '@tomtom-org/maps-sdk/map';
import { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';

const geometries = await GeometriesModule.create(map);

geometries.events.on('shown-features', (features) => {
    const bbox = bboxFromGeoJSON(features);
    if (bbox) {
        map.mapLibreMap.fitBounds(bbox, { padding: 40 });
    }
});

Cleanup

Unsubscribe — remove a single handler

Each events.on() call returns an unsubscribe function. Call it to stop receiving events from that specific handler without affecting any others:

const unsubA = places.events.on('config-change', handlerA);
const unsubB = places.events.on('config-change', handlerB);

unsubA(); // removes only handlerA — handlerB keeps firing

off() — remove all handlers for a type

events.off(type) clears every handler registered for that event type in one call. Useful during component teardown when you don't want to track individual unsubscribes:

// Clear all config-change listeners at once
trafficFlow.events.off('config-change');

// Clear all shown-features listeners at once
places.events.off('shown-features');

off accepts all event types — user interaction types (click, hover, etc.) and module lifecycle types (config-change, shown-features) — so the same teardown pattern works uniformly:

// Full cleanup for a component that registered multiple event types
function onUnmount() {
    places.events.off('click');
    places.events.off('hover');
    places.events.off('config-change');
    places.events.off('shown-features');
}

RoutingModule — lifecycle events alongside named scopes

RoutingModule draws several parts of a route, so it exposes a named scope for each. Lifecycle events stay on the module itself, under the same events.on() used everywhere else:

import { RoutingModule } from '@tomtom-org/maps-sdk/map';

const routing = await RoutingModule.create(map);

// Module lifecycle events
const unsubConfig = routing.events.on('config-change', (config) => {
    console.log('Routing config changed:', config);
});

const unsubShown = routing.events.on('shown-features', (features) => {
    if ('routes' in features) {
        console.log('Routes shown:', features.routes);
    } else {
        console.log('Waypoints shown:', features.waypoints);
    }
});

// User interaction events — module-wide, or through a named scope
routing.events.mainLines.on('click', (route) => { /* ... */ });
routing.events.waypoints.on('hover', (waypoint) => { /* ... */ });

Related guides