Skip to content
Display a Popup on Hover

Display a Popup on Hover in Maptoolkit Maps JS

Hover popups in Maptoolkit Maps JS use the mouseenter and mouseleave events on a layer to show and hide a Popup positioned at the hovered feature’s coordinates. The cursor style is also updated to a pointer to signal interactivity. Use this pattern to display tooltips, data previews, or feature labels for any point, line, or polygon layer where a click would be too heavy an interaction.

const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center: [12.0, 47.5],
        zoom: 7,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    const popup = new maptoolkit.Popup({ closeButton: false, closeOnClick: false });

    const cities = {
        type: 'FeatureCollection',
        features: [
            { type: 'Feature', geometry: { type: 'Point', coordinates: [11.39085, 47.27574] }, properties: { name: 'Innsbruck', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [16.3738, 48.2082] }, properties: { name: 'Vienna', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [13.0550, 47.8095] }, properties: { name: 'Salzburg', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [15.4395, 47.0707] }, properties: { name: 'Graz', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [11.576, 48.1374] }, properties: { name: 'Munich', country: 'Germany' } },
        ]
    };

    map.on('load', () => {
        map.addSource('cities', { type: 'geojson', data: cities });
        map.addLayer({
            id: 'cities',
            type: 'circle',
            source: 'cities',
            paint: { 'circle-radius': 8, 'circle-color': '#3887be' }
        });

        map.on('mouseenter', 'cities', (e) => {
            map.getCanvas().style.cursor = 'pointer';
            const coordinates = e.features[0].geometry.coordinates.slice();
            const { name, country } = e.features[0].properties;
            popup.setLngLat(coordinates).setHTML(`<strong>${name}</strong><br>${country}`).addTo(map);
        });

        map.on('mouseleave', 'cities', () => {
            map.getCanvas().style.cursor = '';
            popup.remove();
        });
    });
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Display a Popup on Hover - Maptoolkit Maps JS</title>
    <meta property="og:description" content="Display a popup when hovering over a point feature on the map." />
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.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 maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center: [12.0, 47.5],
        zoom: 7,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    const popup = new maptoolkit.Popup({ closeButton: false, closeOnClick: false });

    const cities = {
        type: 'FeatureCollection',
        features: [
            { type: 'Feature', geometry: { type: 'Point', coordinates: [11.39085, 47.27574] }, properties: { name: 'Innsbruck', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [16.3738, 48.2082] }, properties: { name: 'Vienna', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [13.0550, 47.8095] }, properties: { name: 'Salzburg', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [15.4395, 47.0707] }, properties: { name: 'Graz', country: 'Austria' } },
            { type: 'Feature', geometry: { type: 'Point', coordinates: [11.576, 48.1374] }, properties: { name: 'Munich', country: 'Germany' } },
        ]
    };

    map.on('load', () => {
        map.addSource('cities', { type: 'geojson', data: cities });
        map.addLayer({
            id: 'cities',
            type: 'circle',
            source: 'cities',
            paint: { 'circle-radius': 8, 'circle-color': '#3887be' }
        });

        map.on('mouseenter', 'cities', (e) => {
            map.getCanvas().style.cursor = 'pointer';
            const coordinates = e.features[0].geometry.coordinates.slice();
            const { name, country } = e.features[0].properties;
            popup.setLngLat(coordinates).setHTML(`<strong>${name}</strong><br>${country}`).addTo(map);
        });

        map.on('mouseleave', 'cities', () => {
            map.getCanvas().style.cursor = '';
            popup.remove();
        });
    });
</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.

Use the Maptoolkit Connector. Create an interactive map with zoom level 7, centered around [12.0, 47.5]. Add circle markers for Innsbruck, Vienna, Salzburg, Graz, and Munich. Hovering over a marker should display a popup with the city name and country.

How it works

One popup instance is created up front and reused. mouseenter positions and shows it, mouseleave removes it. Creating a popup per event would leave a trail of them behind the cursor.

closeButton: false and closeOnClick: false both matter for hover. A close button is pointless when the popup disappears on mouseleave, and the default close-on-click would dismiss it the moment the user clicks the feature it describes.

mouseenter and mouseleave are registered against a layer id, so they fire on entering and leaving a feature rather than the map. The map-level equivalent would fire once for the whole canvas.

Changing the cursor to a pointer on enter is what signals interactivity.

Hover has no touch equivalent. On a phone there is no mouseenter, so anything reachable only by hover is unreachable there; provide a click path as well.

Next steps

Hover suits a preview rather than detail, so the pairing to build next is hover for the name and click for everything else. That also gives touch users a path, since hover does not exist for them.

Highlighting the hovered feature as well as labelling it makes the connection obvious when features are close together, and keeping the popup near the cursor rather than the feature centre helps on long or oddly shaped geometries.