Map draws an interactive map with MapLibre GL on OpenFreeMap tiles: OpenStreetMap data, no API key, no account and no view limit. The basemap switches between light and dark with the theme, and its background takes the page background token, so a map sits on the page instead of on top of it. Markers are React content, so prices, avatars and buttons on the map are ordinary Once UI components.
maplibre-gl is an optional peer dependency, like recharts for the charts. Install it next to Once UI and import the map from its own entry point, so an app without a map never loads it:
Positions are [longitude, latitude], the order MapLibre and GeoJSON use. The map renders on the client only; on the server and until MapLibre loads it draws an empty frame of the same size, so the page does not shift.
MapMarkers takes your items (each with an id and a position) and renders one marker per item. cluster merges markers that overlap into a count that zooms in when pressed. activeId lifts one marker above the rest, for the item hovered in a result list. bounds frames a list of positions and frames it again whenever the list changes, so a new search is always in view.
MapPin is the marker Once UI draws: a pill with a label, or a dot without one. With href it is a link and with onClick a button, so it is reachable by Tab; tabbing onto a pin outside the view pans it in. MapPopup is a surface card above a point; render it while it is open, and Escape or its close button call onClose.
MapMarker pins whatever you give it. anchor says which part of the content touches the point: bottom (the default) for a pin, center for an avatar.
MapCircle marks an area on the ground, sized in metres, in a token colour. It scales with the map, which makes it the honest way to show an approximate address.
viewport, defaultViewport and onViewportChange follow the same contract as every other controlled prop in Once UI. onViewportChange fires when a pan or zoom ends, which is also the moment to search the visible area.
The default style is free, but you can point mapStyle at any MapLibre style: a URL or a style object, or a { light, dark } pair that follows the theme. A provider that needs a key usually takes it in the style URL; one that wants a header gets it through transformRequest.
Keep the attribution the provider asks for: the map shows the attribution each style declares in its bottom corner. Pass tint={false} to keep a style's own background colour.
The map is a focusable region: arrow keys pan, + and - zoom, and the frame shows a focus ring. Pins with href or onClick are links and buttons in the tab order. With reduced motion on, fitting, zooming and following a controlled viewport jump instead of animating, and pins do not scale on hover. When WebGL is off, the frame says the map could not load instead of staying blank; set the text with labels.
useMap() inside a Map returns the MapLibre instance and the loaded module, and onMapLoad hands you the instance once the first style has loaded, for layers the components do not cover (routes, heatmaps, GeoJSON).