@minmaps-dev/mm-web-sdk
v1.0.0-rc.3
Published
MinuteMaps indoor mapping SDK for MapLibre GL - render venues, floors, POIs, and wayfinding routes
Readme
MinuteMaps Web SDK
@minmaps-dev/mm-web-sdk is the official MinuteMaps SDK for the web. It renders indoor venues — floors, POIs, amenities, destinations, and wayfinding routes — on top of MapLibre GL JS, with data sourced from JACS.
Features
- Venue + floors — load a venue by
customerId/venueId, switch active floor, and let the SDK manage layer visibility. - POIs, amenities, destinations — query the venue, filter by floor, and run keyword searches.
- Wayfinding — compute kiosk-to-destination or waypoint-to-waypoint routes and render them on the map.
- View modes — toggle 3D, flat (top-down), and 2D-units modes at runtime.
- Camera control —
setView/resetView/getCameraPosition, plus a livecameraChangeevent for compass UIs. - React entrypoint — drop-in
<MinuteMapsView />for React 18/19 apps. - JACS proxy or direct mode — keep credentials server-side with the proxy mode, or call JACS directly from trusted environments.
Installation
npm install @minmaps-dev/mm-web-sdk maplibre-gl @turf/turfmaplibre-gl (v4) and @turf/turf (v7) are peer dependencies. react / react-dom (v18 or v19) are optional peers, only required if you use the /react entrypoint.
Don't forget to import the MapLibre stylesheet once in your app:
import 'maplibre-gl/dist/maplibre-gl.css'Quick start (vanilla)
import { MinuteMaps } from '@minmaps-dev/mm-web-sdk'
import 'maplibre-gl/dist/maplibre-gl.css'
const sdk = new MinuteMaps({
container: 'map', // element or element id
jmap: {
host: '', // unused in proxy mode
customerId: 123,
venueId: 456,
},
jacs: {
mode: 'proxy', // recommended for browser apps
proxyBaseUrl: '/api/jacs',
},
options: {
customSprite: '/sprites/sprite', // your icon sprite (no extension)
minIndoorZoom: 16,
debug: true,
styleMode: 'sdkTemplate',
},
})
sdk.on('ready', ({ venue }) => {
console.log('venue ready', venue?.name)
})
await sdk.init()Quick start (React)
import { MinuteMapsView } from '@minmaps-dev/mm-web-sdk/react'
import 'maplibre-gl/dist/maplibre-gl.css'
export function Map() {
return (
<MinuteMapsView
className="h-screen w-screen"
config={{
container: '', // ignored — the component owns the element
jmap: { host: '', customerId: 123, venueId: 456 },
jacs: { mode: 'proxy', proxyBaseUrl: '/api/jacs' },
options: { styleMode: 'sdkTemplate' },
}}
/>
)
}For a more involved integration that wires events, floors, search, and wayfinding into React state, see the mm-web-sdk-example Next.js app — particularly hooks/useMap.ts.
Configuration
type SDKConfig = {
container: HTMLElement | string
jmap: {
host: string
customerId: number
venueId: number
locale?: string
auth?: { clientId: string; clientSecret: string }
}
jacs: {
mode: 'proxy' | 'direct'
host?: string // 'direct' only
auth?: { clientId: string; username: string; password: string } // 'direct' only
proxyBaseUrl?: string // default '/api/jacs'
}
options?: SDKOptions
}
type SDKOptions = {
debug?: boolean
initialFloor?: string | number
customSprite?: string // sprite URL prefix (no extension)
minIndoorZoom?: number
boundsPadding?: number // default 50
styleMode?: 'venueStyleUrl' | 'sdkTemplate'
templateOverrideMode?: 'colorsOnly' | 'colorsAndConstants' | 'all'
}JACS proxy mode (recommended)
Browser apps should use mode: 'proxy' and forward requests through your own server so JACS credentials never reach the client. The example app ships a Next.js route handler at app/api/jacs/[...path]/route.ts that:
- Reads
JACS_HOST,JACS_CLIENT_ID,JACS_USERNAME,JACS_PASSWORDfrom server env. - Exchanges the password grant for a bearer token (cached until expiry).
- Forwards
GET /api/jacs/<path>to${JACS_HOST}/JACS/api/<path>with the token attached.
Copy that handler into your own backend (or adapt it to your framework) and point jacs.proxyBaseUrl at it.
JACS direct mode
For trusted environments (server-rendered pages, Electron kiosks, internal tools) you can call JACS directly:
jacs: {
mode: 'direct',
host: 'https://jacs.example.com',
auth: { clientId, username, password },
}API
Class MinuteMaps
new MinuteMaps(config: SDKConfig)
sdk.init(): Promise<void>
sdk.destroy(): void
sdk.isReady(): boolean
sdk.getMap(): maplibregl.Map | nullcreateMinuteMapsSDK(config) is a factory shorthand for new MinuteMaps(config).
Floors
sdk.getFloors(): Floor[]
sdk.getCurrentFloor(): Floor | null
sdk.getDefaultFloor(): Floor | null
sdk.setCurrentFloor(floor: Floor): Promise<void>POIs, destinations, search
sdk.getAllPOIs(floor?: Floor): POI[]
sdk.getDestinations(floor?: Floor): Destination[]
sdk.searchPOIs(query: string, floor?: Floor): POI[]
sdk.getYouAreHerePOI(floor?: Floor): POI | null
sdk.getYouAreHereCoordinates(floor?: Floor): [number, number] | nullAmenities (sdk.amenities)
sdk.amenities.getAll(): AmenityWithFloor[]
sdk.amenities.getByFloorId(floorId): AmenityWithFloor[]
sdk.amenities.getAllKiosks(): AmenityWithFloor[]
sdk.amenities.getKioskForFloor(floorId): AmenityWithFloor | nullWayfinding
sdk.navigateFromKioskToDestination(destination): Promise<...>
sdk.wayfindBetweenWaypoints(from, to, opts?: {
centerMode?: 'none' | 'destination' | 'route'
zoom?: number
}): Promise<...>
sdk.clearRoute(): voidCamera + view modes
sdk.setView({ center?, zoom?, pitch?, bearing?, animate?, duration? })
sdk.resetView({ animate?, duration? })
sdk.getCameraPosition(): CameraState | null
sdk.set3dEnabled(enabled) / toggle3d() / getIs3dEnabled()
sdk.setUnits2dEnabled(enabled) / toggleUnits2d() / getIsUnits2dEnabled()
sdk.setFlatMode(enabled) / toggleFlatMode() / getIsFlatMode()MapLibre passthroughs
sdk.addControl(control, position?) // forwards to the underlying MapLibre map
sdk.getMap() // escape hatch: the raw maplibregl.MapEvents
Subscribe with sdk.on(event, cb) / unsubscribe with sdk.off(event, cb).
| Event | Payload | When |
| --------------- | ----------------------------- | ---------------------------------------------------------- |
| ready | { venue } | Map loaded and the initial floor is rendered. |
| floorsLoaded | {} | All floor geojson is available. |
| floorChanged | { floor } | After setCurrentFloor resolves. |
| cameraChange | { camera: CameraState } | On every map move (drives compass/heading UI). |
| error | { error } | Init or runtime error. |
Sprites
Pass options.customSprite as a sprite URL prefix without the extension (MapLibre will append .json and .png / @2x). The example app serves its sprite from public/sprites/.
Development
npm install
npm run dev:sdk # rollup --watch
npm run build # clean + production build
npm run typecheck # tsc --noEmitThe build emits ESM, CJS, and types under dist/ for both the root entry and the /react entry.
License
MIT — see LICENSE.
