Display a Popup on Click in MapLibre GL JS
Showing a popup on any map click requires listening to the map’s click event and using the event’s lngLat property to position a new Popup at the cursor’s geographic coordinates. The popup is removed before a new one is created so only one is visible at a time. Use this pattern as the foundation for coordinate pickers, location inspectors, or any tool where clicking the map should reveal information about that point.
const API_KEY = 'YOUR_API_KEY';
const map = new maplibregl.Map({
container: 'map',
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [11.39085, 47.27574],
zoom: 12,
attributionControl: { compact: false }
});
map.addControl(new maplibregl.NavigationControl(), 'top-right');
map.on('click', (e) => {
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`<strong>Coordinates:</strong><br>${e.lngLat.lng.toFixed(5)}, ${e.lngLat.lat.toFixed(5)}`)
.addTo(map);
});
import { LogoControl } from "https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-logo-control@1.1.2/dist/maplibre-logo-control.js";
map.addControl(new LogoControl({ variant: "com" }));<!DOCTYPE html>
<html lang="en">
<head>
<title>Display a Popup on Click - MapLibre GL JS</title>
<meta property="og:description" content="Display a popup with coordinates when clicking anywhere on the map." />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.1/dist/maplibre-gl.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.1/dist/maplibre-gl.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-logo-control@1.1.2/dist/maplibre-logo-control.css" />
<style>
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
#map { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const API_KEY = 'YOUR_API_KEY';
const map = new maplibregl.Map({
container: 'map',
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [11.39085, 47.27574],
zoom: 12,
attributionControl: { compact: false }
});
map.addControl(new maplibregl.NavigationControl(), 'top-right');
map.on('click', (e) => {
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`<strong>Coordinates:</strong><br>${e.lngLat.lng.toFixed(5)}, ${e.lngLat.lat.toFixed(5)}`)
.addTo(map);
});
</script>
<script type="module">
import { LogoControl } from "https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-logo-control@1.1.2/dist/maplibre-logo-control.js";
map.addControl(new LogoControl({ variant: "com" }));
</script>
</body>
</html>Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.
How it works
The handler listens on the map rather than a layer, so it fires for any click, including empty ocean. That is deliberate here: the popup reports the coordinate, which exists everywhere.
e.lngLat is the geographic position of the click, already converted from pixels. The
companion e.point gives the pixel position, which is what queryRenderedFeatures needs.
This creates a new popup on every click. Only one is ever open, because a popup closes
itself on the next map click (closeOnClick defaults to true), so the old one goes away as the
new one opens. To reuse a single instance instead and move it with setLngLat, create it with
closeOnClick: false: otherwise its own click listener runs after yours and closes it again on
every second click.
Next steps
Reporting a coordinate is a demonstration; a click is usually meant to find something. The next step is querying what was clicked and showing that instead, which turns the popup from a readout into an answer.
If the coordinate is the point, the Geocoding API turns it into an address and the Elevation API into a height, both of which are more useful in a popup than six decimal places.