Documentation menu
Example
Click a feature for a popup
Click one of your points and get a popup built from its properties, with a pointer cursor on hover so people know it is clickable. The text goes in with setText, so a property holding markup is shown as text, never run.
ts
import { createMap } from "@mapmap/maps";
import * as maplibregl from "maplibre-gl";
const map = createMap({
container: "map",
apiKey: "snk_...",
attributionPosition: "bottom-left",
center: [-1.6, 53.2],
zoom: 6,
});
await map.whenReady();
map.map.addSource("sites", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{ type: "Feature", properties: { name: "London depot", open: "06:00 to 22:00" },
geometry: { type: "Point", coordinates: [-0.1278, 51.5074] } },
{ type: "Feature", properties: { name: "Birmingham yard", open: "24 hours" },
geometry: { type: "Point", coordinates: [-1.8904, 52.4862] } },
{ type: "Feature", properties: { name: "Leeds cross-dock", open: "05:00 to 21:00" },
geometry: { type: "Point", coordinates: [-1.5491, 53.8008] } },
],
},
});
// Slots arrive in the SDK 0.17: map.slot("middle") replaces "road-labels".
map.map.addLayer({
id: "sites",
type: "circle",
source: "sites",
paint: {
"circle-radius": 8,
"circle-color": "#1a6bff",
"circle-stroke-color": "#ffffff",
"circle-stroke-width": 2,
},
}, "road-labels");
const popup = new maplibregl.Popup({ closeButton: true, offset: 12 });
map.map.on("click", "sites", (e) => {
const feature = e.features?.[0];
if (!feature || feature.geometry.type !== "Point") return;
const { name, open } = feature.properties;
popup
.setLngLat(feature.geometry.coordinates as [number, number])
.setText(name + ", open " + open) // text, never HTML from your data
.addTo(map.map);
});
map.map.on("mouseenter", "sites", () => {
map.map.getCanvas().style.cursor = "pointer";
});
map.map.on("mouseleave", "sites", () => {
map.map.getCanvas().style.cursor = "";
});Worth knowing
- Use the point's own coordinates rather than e.lngLat, so the popup sits on the feature and not wherever inside the circle the click landed.
- For richer content build an element and pass it to setDOMContent. Reach for setHTML only with markup you wrote yourself, never with values from your data.
- For points with clustering, popups and nearest-place answers already wired, PlacesLayer does all of this in one call.