Map

MapLibre GL primitives. Ships on the `@aeontel/ui/map` subpath — heavy, lazy by design. Pair with `@aeontel/ui/styles/map` for popup overrides.

Usage

TSX
import {
  Map,
  MapControls,
  MapMarker,
  MarkerContent,
  MarkerPopup,
} from "@aeontel/ui/map";

export default function Sites() {
  return (
    <div className="h-96 w-full">
      {/* The viewport prop is `viewport`, not `initialViewState`. */}
      {/* Shape: { center: [lng, lat], zoom } — center is a tuple. */}
      <Map viewport={{ center: [-122.4, 37.78], zoom: 11 }}>
        <MapControls position="top-right" />
        {/* Default blue dot + click popup */}
        <MapMarker longitude={-122.4} latitude={37.78}>
          <MarkerPopup>San Francisco HQ</MarkerPopup>
        </MapMarker>
        {/* Custom marker visual — wrap in MarkerContent. Raw children */}
        {/* of MapMarker are not portaled into the marker DOM and stay */}
        {/* invisible. */}
        <MapMarker longitude={2.3522} latitude={48.8566}>
          <MarkerContent>
            <div className="bg-primary text-primary-foreground rounded-full px-2 py-1 text-xs font-semibold shadow">
              Paris
            </div>
          </MarkerContent>
        </MapMarker>
      </Map>
    </div>
  );
}

// The container needs an explicit height — MapLibre measures its parent.
// Pair every map with `@import "@aeontel/ui/styles/map"` in your root CSS
// (only affects popup chrome — does NOT gate marker visibility).

Exports

Map

TypeScript
Map(props: ComponentProps): JSX.Element

Props

PropTypeDefaultDescription
children?ReactNode
className?string | undefinedAdditional CSS classes for the map container
theme?Theme | undefinedTheme for the map. If not provided, automatically detects system preference. Pass your theme value here.
styles?{ light?: MapStyleOption; dark?: MapStyleOption; } | undefinedCustom map styles for light and dark themes. Overrides the default Carto styles.
projection?MapLibreGL.ProjectionSpecification | undefinedMap projection type. Use { type: "globe" } for 3D globe view.
viewport?Partial<MapViewport> | undefinedControlled viewport. When provided with onViewportChange, the map becomes controlled and viewport is driven by this prop.
onViewportChange?((viewport: MapViewport) => void) | undefinedCallback fired continuously as the viewport changes (pan, zoom, rotate, pitch). Can be used standalone to observe changes, or with viewport prop to enable controlled mode where the map viewport is driven by your state.
loading?boolean | undefinedfalseShow a loading indicator on the map

MapMarker

TypeScript
MapMarker(props: MapMarkerProps): JSX.Element

Props

PropTypeDefaultDescription
longitudenumberLongitude coordinate for marker position
latitudenumberLatitude coordinate for marker position
childrenReactNodeMarker subcomponents (MarkerContent, MarkerPopup, MarkerTooltip, MarkerLabel)
onClick?((e: MouseEvent) => void) | undefinedCallback when marker is clicked
onMouseEnter?((e: MouseEvent) => void) | undefinedCallback when mouse enters marker
onMouseLeave?((e: MouseEvent) => void) | undefinedCallback when mouse leaves marker
onDragStart?((lngLat: { lng: number; lat: number; }) => void) | undefinedCallback when marker drag starts (requires draggable: true)
onDrag?((lngLat: { lng: number; lat: number; }) => void) | undefinedCallback during marker drag (requires draggable: true)
onDragEnd?((lngLat: { lng: number; lat: number; }) => void) | undefinedCallback when marker drag ends (requires draggable: true)

MarkerContent

TypeScript
MarkerContent(props: MarkerContentProps): JSX.Element

Props

PropTypeDefaultDescription
children?ReactNodeCustom marker content. Defaults to a blue dot if not provided
className?string | undefinedAdditional CSS classes for the marker container

MarkerLabel

TypeScript
MarkerLabel(props: MarkerLabelProps): JSX.Element

Props

PropTypeDefaultDescription
childrenReactNodeLabel text content
className?string | undefinedAdditional CSS classes for the label
position?"top" | "bottom" | undefined"top"Position of the label relative to the marker (default: "top")

MarkerPopup

TypeScript
MarkerPopup(props: MarkerPopupProps): JSX.Element

Props

PropTypeDefaultDescription
childrenReactNodePopup content
className?string | undefinedAdditional CSS classes for the popup container
closeButton?boolean | undefinedfalseShow a close button in the popup (default: false)

MarkerTooltip

TypeScript
MarkerTooltip(props: MarkerTooltipProps): JSX.Element

Props

PropTypeDefaultDescription
childrenReactNodeTooltip content
className?string | undefinedAdditional CSS classes for the tooltip container

MapPopup

TypeScript
MapPopup(props: MapPopupProps): JSX.Element

Props

PropTypeDefaultDescription
longitudenumberLongitude coordinate for popup position
latitudenumberLatitude coordinate for popup position
onClose?(() => void) | undefinedCallback when popup is closed
childrenReactNodePopup content
className?string | undefinedAdditional CSS classes for the popup container
closeButton?boolean | undefinedfalseShow a close button in the popup (default: false)

MapControls

TypeScript
MapControls(props: MapControlsProps): JSX.Element

Props

PropTypeDefaultDescription
position?"top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined"bottom-right"Position of the controls on the map (default: "bottom-right")
showZoom?boolean | undefinedtrueShow zoom in/out buttons (default: true)
showCompass?boolean | undefinedfalseShow compass button to reset bearing (default: false)
showLocate?boolean | undefinedfalseShow locate button to find user's location (default: false)
showFullscreen?boolean | undefinedfalseShow fullscreen toggle button (default: false)
className?string | undefinedAdditional CSS classes for the controls container
onLocate?((coords: { longitude: number; latitude: number; }) => void) | undefinedCallback with user coordinates when located

MapRoute

TypeScript
MapRoute(props: MapRouteProps): JSX.Element

Props

PropTypeDefaultDescription
id?string | undefinedOptional unique identifier for the route layer
coordinates[number, number][]Array of [longitude, latitude] coordinate pairs defining the route
color?string | undefined"#4285F4"Line color as CSS color value (default: "#4285F4")
width?number | undefined3Line width in pixels (default: 3)
opacity?number | undefined0.8Line opacity from 0 to 1 (default: 0.8)
dashArray?[number, number] | undefinedDash pattern [dash length, gap length] for dashed lines
onClick?(() => void) | undefinedCallback when the route line is clicked
onMouseEnter?(() => void) | undefinedCallback when mouse enters the route line
onMouseLeave?(() => void) | undefinedCallback when mouse leaves the route line
interactive?boolean | undefinedtrueWhether the route is interactive - shows pointer cursor on hover (default: true)

MapArc

TypeScript
MapArc(props: MapArcProps<T>): JSX.Element

Props

PropTypeDefaultDescription
dataT[]Array of arcs to render. Each arc must have a unique id.
id?string | undefinedOptional unique identifier prefix for the arc source/layers. Auto-generated if not provided.
curvature?number | undefinedDEFAULT_ARC_CURVATUREHow far each arc bows away from a straight line. 0 renders straight lines; higher values bend further. Negative values bend to the opposite side. Arcs are computed as a quadratic Bézier in lng/lat space and do not account for the antimeridian. (default: 0.2)
samples?number | undefinedDEFAULT_ARC_SAMPLESNumber of samples used to render each curve. Higher = smoother. (default: 64)
paint?{ "line-opacity"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-opacity-transition"?: MapLibreGL.TransitionSpecification; "line-color"?: MapLibreGL.DataDrivenPropertyValueSpecification<MapLibreGL.ColorSpecification>; "line-color-transition"?: MapLibreGL.TransitionSpecification; "line-translate"?: MapLibreGL.PropertyValueSpecification<[number, number]>; "line-translate-transition"?: MapLibreGL.TransitionSpecification; "line-translate-anchor"?: MapLibreGL.PropertyValueSpecification<"map" | "viewport">; "line-width"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-width-transition"?: MapLibreGL.TransitionSpecification; "line-gap-width"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-gap-width-transition"?: MapLibreGL.TransitionSpecification; "line-offset"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-offset-transition"?: MapLibreGL.TransitionSpecification; "line-blur"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-blur-transition"?: MapLibreGL.TransitionSpecification; "line-dasharray"?: MapLibreGL.DataDrivenPropertyValueSpecification<Array<number>>; "line-dasharray-transition"?: MapLibreGL.TransitionSpecification; "line-pattern"?: MapLibreGL.DataDrivenPropertyValueSpecification<MapLibreGL.ResolvedImageSpecification>; "line-pattern-transition"?: MapLibreGL.TransitionSpecification; "line-gradient"?: MapLibreGL.ExpressionSpecification; } | undefinedMapLibre paint properties for the arc layer. Merged on top of sensible defaults (line-color: #4285F4, line-width: 2, line-opacity: 0.85). Any value can be a MapLibre expression for per-feature styling, every field on each arc datum (besides from/to) is exposed via ["get", ...].
layout?{ "line-cap"?: MapLibreGL.DataDrivenPropertyValueSpecification<"butt" | "round" | "square">; "line-join"?: MapLibreGL.DataDrivenPropertyValueSpecification<"bevel" | "round" | "miter">; "line-miter-limit"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-round-limit"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-sort-key"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; visibility?: MapLibreGL.VisibilitySpecification; } | undefinedMapLibre layout properties for the arc layer. Defaults to rounded joins/caps.
hoverPaint?{ "line-opacity"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-opacity-transition"?: MapLibreGL.TransitionSpecification; "line-color"?: MapLibreGL.DataDrivenPropertyValueSpecification<MapLibreGL.ColorSpecification>; "line-color-transition"?: MapLibreGL.TransitionSpecification; "line-translate"?: MapLibreGL.PropertyValueSpecification<[number, number]>; "line-translate-transition"?: MapLibreGL.TransitionSpecification; "line-translate-anchor"?: MapLibreGL.PropertyValueSpecification<"map" | "viewport">; "line-width"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-width-transition"?: MapLibreGL.TransitionSpecification; "line-gap-width"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-gap-width-transition"?: MapLibreGL.TransitionSpecification; "line-offset"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-offset-transition"?: MapLibreGL.TransitionSpecification; "line-blur"?: MapLibreGL.DataDrivenPropertyValueSpecification<number>; "line-blur-transition"?: MapLibreGL.TransitionSpecification; "line-dasharray"?: MapLibreGL.DataDrivenPropertyValueSpecification<Array<number>>; "line-dasharray-transition"?: MapLibreGL.TransitionSpecification; "line-pattern"?: MapLibreGL.DataDrivenPropertyValueSpecification<MapLibreGL.ResolvedImageSpecification>; "line-pattern-transition"?: MapLibreGL.TransitionSpecification; "line-gradient"?: MapLibreGL.ExpressionSpecification; } | undefinedPaint properties applied to the arc currently under the cursor. Each key is merged into paint as a case expression keyed on per-feature hover state, so only the hovered arc changes appearance.
onClick?((e: MapArcEvent<T>) => void) | undefinedCallback when an arc is clicked.
onHover?((e: MapArcEvent<T> | null) => void) | undefinedCallback fired when the hovered arc changes. Receives the cursor's lng/lat at the moment of entry, and null when the cursor leaves the last hovered arc.
interactive?boolean | undefinedtrueWhether arcs respond to mouse events (default: true).
beforeId?string | undefinedOptional MapLibre layer id to insert the arc layers before (z-order control).

MapClusterLayer

TypeScript
MapClusterLayer(props: MapClusterLayerProps<P>): JSX.Element

Props

PropTypeDefaultDescription
datastring | import("/home/runner/work/aeontel/aeontel/node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index").FeatureCollection<import("/home/runner/work/aeontel/aeontel/node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index").Point, P>GeoJSON FeatureCollection data or URL to fetch GeoJSON from
clusterMaxZoom?number | undefined14Maximum zoom level to cluster points on (default: 14)
clusterRadius?number | undefined50Radius of each cluster when clustering points in pixels (default: 50)
clusterColors?[string, string, string] | undefined["#22c55e", "#eab308", "#ef4444"]Colors for cluster circles: [small, medium, large] based on point count (default: ["#22c55e", "#eab308", "#ef4444"])
clusterThresholds?[number, number] | undefined[100, 750]Point count thresholds for color/size steps: [medium, large] (default: [100, 750])
pointColor?string | undefined"#3b82f6"Color for unclustered individual points (default: "#3b82f6")
onPointClick?((feature: import("/home/runner/work/aeontel/aeontel/node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index").Feature<import("/home/runner/work/aeontel/aeontel/node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index").Point, P>, coordinates: [number, number]) => void) | undefinedCallback when an unclustered point is clicked
onClusterClick?((clusterId: number, coordinates: [number, number], pointCount: number) => void) | undefinedCallback when a cluster is clicked. If not provided, zooms into the cluster

useMap

TypeScript
useMap(props: ComponentProps): JSX.Element