A state-machine-powered layer management library for map applications. Model your map contents as layers and layer groups, then control visibility, opacity, and ordering from any UI framework and any mapping library.
| Package | Version | Description |
|---|---|---|
@ulm/core |
Core state machine library — framework and map-library agnostic | |
@ulm/leaflet |
Leaflet adapter — syncs manager state to a Leaflet map |
LayerTimeInfo using @internationalized/date for date ranges.LayerManagerAdapter to connect any mapping library.npm install @ulm/core
import { LayerManager } from '@ulm/core';
interface LayerData {
url: string;
}
const manager = new LayerManager<LayerData>({
onLayerAdded(info) {
console.log('added:', info.layerId);
},
onVisibilityChanged(info, visible) {
console.log(info.layerId, 'visible:', visible);
},
});
manager.addLayer({
layerConfig: {
layerId: 'basemap',
layerName: 'Basemap',
layerType: 'layer',
parentId: null,
layerData: { url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' },
},
visible: true,
});
manager.setVisibility('basemap', false);
manager.destroy();
See @ulm/core for the full API reference.
createLayerManagerMachine is exported for direct XState usage (e.g. integrating with @xstate/react):
import { createLayerManagerMachine } from '@ulm/core';
import { createActor } from 'xstate';
const actor = createActor(createLayerManagerMachine<LayerData>(), {
input: { allowNestedGroupLayers: true },
});
actor.start();
If you're using LayerManager, the raw actor is also available via manager.actor.
LayerManager owns state; adapters subscribe to its emitted events and perform map-library side-effects (add/remove layers, sync visibility, opacity). Implement the LayerManagerAdapter interface and attach it with manager.setAdapter(adapter).
import { LeafletLayerManagerAdapter } from '@ulm/leaflet';
manager.setAdapter(new LeafletLayerManagerAdapter(map));
// manager.setAdapter(null) detaches and calls adapter.unregister()
See @ulm/leaflet for a complete example, or implement LayerManagerAdapter yourself for other mapping libraries (OpenLayers, Mapbox, etc.).
| Example | Description |
|---|---|
examples/simple |
Minimal vanilla TypeScript — demonstrates LayerManager with plain DOM |
examples/leaflet |
React + Leaflet — @ulm/leaflet adapter, nested groups, layer list UI |
To run an example:
npm install # install all workspace dependencies
npm run dev # starts all dev servers via Turbo
packages/
core/ @ulm/core — state machine library
leaflet/ @ulm/leaflet — Leaflet adapter
examples/
simple/ vanilla TypeScript example
leaflet/ React + Leaflet example