Props, hooks, and shared types for all map components. Built on MapLibre GL JS.
Root container that initializes MapLibre GL and provides context to child components. Handles theme switching between light and dark modes automatically.
Extends MapOptions from MapLibre GL (excluding container, style).
childrenReactNode—theme"light" | "dark"autostyles{ light?: string | StyleSpecification; dark?: string | StyleSpecification }CARTOprojectionProjectionSpecificationmercatorattributionboolean | "compact" | "full""compact"onClick(e: MapMouseEvent) => void—onMoveEnd(e: MapLibreEvent) => void—onZoomEnd(e: MapLibreEvent) => void—onLoad(map: MapLibreGL.Map) => void—Hook that provides access to the MapLibre map instance and loading state. Must be used within a Map component.
Returns map (MapLibre.Map | null) and isLoaded (boolean).
Renders map control buttons (zoom, compass, locate, fullscreen). Must be inside Map.
position"top-left" | "top-right" | "bottom-left" | "bottom-right""bottom-right"showZoombooleantrueshowCompassbooleanfalseshowLocatebooleanfalseshowFullscreenbooleanfalseclassNamestring—onLocate(coords: { longitude: number; latitude: number; accuracy: number }) => void—onLocateError(error: GeolocationPositionError | null) => void—showAccuracybooleanfalseaccuracyStylesAccuracyCircleStyles—accuracyTimeoutnumber10000Container for marker-related components. Provides context for children and handles positioning.
Extends MarkerOptions from MapLibre GL (excluding element).
longitudenumber—latitudenumber—childrenReactNode—onClick(e: MouseEvent) => void—onMouseEnter(e: MouseEvent) => void—onMouseLeave(e: MouseEvent) => void—onDragStart(lngLat: {lng, lat}) => void—onDrag(lngLat: {lng, lat}) => void—onDragEnd(lngLat: {lng, lat}) => void—ariaLabelstring—Renders the visual content of a marker. Must be inside MapMarker. Defaults to a blue dot if no children.
childrenReactNode—variant"pin" | "dot" | "pulse" | "numbered" | "custom""pin"size"sm" | "md" | "lg""md"color"primary" | "destructive" | "success" | "warning" | "muted""primary"animatebooleanfalsenumbernumber—classNamestring—Popup attached to the marker, opens on click. Must be inside MapMarker.
Extends PopupOptions from MapLibre GL (excluding className, closeButton).
childrenReactNode—classNamestring—closeButtonbooleanfalsevariant"default" | "glass""default"Tooltip that appears on hover. Must be inside MapMarker.
Extends PopupOptions from MapLibre GL (excluding className, closeButton, closeOnClick).
childrenReactNode—classNamestring—variant"default" | "glass""default"Text label above or below the marker. Must be inside MarkerContent.
childrenReactNode—classNamestring—position"top" | "bottom""top"Standalone popup placed anywhere on the map without a marker. Must be inside Map.
Extends PopupOptions from MapLibre GL (excluding className, closeButton).
longitudenumber—latitudenumber—onClose() => void—childrenReactNode—classNamestring—closeButtonbooleanfalsevariant"default" | "glass""default"Renders a line/route connecting coordinate points. Must be inside Map. Supports click and hover interactions.
idstringautocoordinates[number, number][]—colorstring#2563ebwidthnumber3opacitynumber0.8dashArray[number, number]—onClick() => void—onMouseEnter() => void—onMouseLeave() => void—interactivebooleantrueClustered point data using MapLibre GL's native clustering. Groups nearby points into clusters that expand on click. Must be inside Map.
datastring | GeoJSON.FeatureCollection—clusterMaxZoomnumber14clusterRadiusnumber50clusterColors[string, string, string]["#51bbd6", "#f1f075", "#f28cb1"]clusterThresholds[number, number][100, 750]pointColorstring"#3b82f6"onPointClick(feature, coordinates) => void—onClusterClick(clusterId, coordinates, pointCount) => voidzoom inDropdown to switch between Standard (vector), Satellite (raster), and Terrain (3D) base map layers. Controlled and uncontrolled modes. Must be inside Map.
positionControlPosition"top-right"defaultLayerMapLayerType"standard"activeLayerMapLayerType—onLayerChange(layer: MapLayerType) => void—satelliteTilesstring[]EsriterrainSourcestring—classNamestring—Extended Components
Native MapLibre heatmap layer for point density visualization. Accepts HeatmapPoint[] or GeoJSON FeatureCollection. Must be inside Map.
@/components/ui/map-heatmap. For GPU-accelerated heatmaps with larger datasets, see .idstring"map-heatmap"pointsHeatmapPoint[]—dataHeatmapPoint[]—weightPropertystring"weight"radiusnumber25intensitynumber | number[]1opacitynumber0.8colorStopsHeatmapColorStop[]—gradientRecord<number, string>heatmapColorPresets.defaultmaxZoomnumber18beforeIdstring—visiblebooleantrueGeocoding search with Nominatim integration, debounced input, keyboard navigation, and auto-fly. Also exports MapSearchInput and MapSearchResults for custom layouts.
@/components/ui/map-search. Uses free Nominatim API — no API key required.placeholderstring"Search location..."onResultSelect(result: SearchResult) => void—maxResultsnumber5minQueryLengthnumber3geocoder(query: string) => Promise<SearchResult[]>NominatimdebounceMsnumber350positionControlPosition"top-left"flyToZoomnumber14classNamestring—defaultExpandedbooleanfalse (mobile) / true (desktop)expandedboolean—onExpandedChange(expanded: boolean) => void—Routing and turn-by-turn directions powered by OSRM. Supports driving, walking, and cycling profiles with multi-waypoint routes.
@/components/ui/map-directions. Default router uses the free OSRM demo server. Provide a custom router prop for production.positionControlPosition"top-right"origin[number, number]—destination[number, number]—waypoints[number, number][]—defaultOrigin[number, number]—defaultDestination[number, number]—defaultProfile"driving" | "walking" | "cycling""driving"onRouteCalculated(route: RouteResult) => void—onError(error: Error) => void—routeColorstringprofile-dependentrouteWidthnumber4showControlsbooleantruefitBoundsbooleantruefitBoundsPaddingnumber50classNamestring—router(origin, destination, profile, waypoints?) => Promise<RouteResult>OSRMgeocoder(query: string) => Promise<GeocodingResult[]>NominatimImport from @/features/maps/lib/hooks.
Subscribe to MapLibre map events with automatic cleanup. Handlers stay fresh via useCallbackRef — no stale closures.
mapMapLibreGL.Map | nullisLoadedbooleaneventkeyof MapEventTypehandler(ev: MapEventType[K]) => voidlayerIdstringDeclaratively manage a GeoJSON source + layer pair. Full lifecycle: add on mount, update reactively, remove on unmount. Re-creates after basemap style switches.
Returns { sourceId: string, layerId: string }
mapMapLibreGL.Map | nullisLoadedbooleanoptions.idstringoptions.typeLayerSpecification['type']options.dataGeoJSON.GeoJSONoptions.paintRecord<string, unknown>options.layoutRecord<string, unknown>options.filterunknown[]options.beforeIdstringoptions.sourceOptionsRecord<string, unknown>Tracks current viewport state. Updates after camera moves, zooms, rotations, and pitch changes.
Returns { center: [lng, lat], zoom: number, bearing: number, pitch: number }
mapMapLibreGL.Map | nullisLoadedbooleanReturns visible map bounds. Updates after every camera move.
Returns [[sw_lng, sw_lat], [ne_lng, ne_lat]] | null
mapMapLibreGL.Map | nullisLoadedbooleanKeeps a stable ref in sync with the latest callback. Essential for MapLibre event handlers that attach on mount and must avoid stale closures.
Returns RefObject<T | undefined>
callbackT | undefinedDetects whether the user prefers reduced motion. Returns true if prefers-reduced-motion: reduce is active. Used by map camera transitions.
Returns boolean
Returns a debounced version of a value. Useful for search inputs and viewport-dependent queries.
Returns T
valueTdelaynumberDebounces a callback function. Unlike useDebouncedValue, debounces the call itself. Cancels pending calls on unmount.
Returns T
callbackTdelaynumberFrom @/lib/map-theme and @/lib/map-hooks.