A context menu plugin for Mapbox GL JS and MapLibre GL JS.
- Context menus for the entire map or scoped to specific layers.
- Menu items, section labels, separators, and nested submenus.
- Customizable items with start/end content slots.
- Click handlers receive map event data.
- Full keyboard navigation.
- Light and dark themes.
npm install mapbox-gl-contextmenu<link
rel="stylesheet"
href="https://unpkg.com/mapbox-gl-contextmenu@1/dist/style.css"
/>
<script src="https://unpkg.com/mapbox-gl-contextmenu@1/dist/index.umd.js"></script>Or using jsDelivr:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/mapbox-gl-contextmenu@1/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/mapbox-gl-contextmenu@1/dist/index.umd.js"></script>import mapboxgl from "mapbox-gl";
import { MapboxContextMenu, ContextMenuItem } from "mapbox-gl-contextmenu";
import "mapbox-gl-contextmenu/style.css";
const map = new mapboxgl.Map({
/* ... */
});
const menu = new MapboxContextMenu({ theme: "auto", width: 180 });
const centerItem = new ContextMenuItem({
label: "Center map here",
start: { className: "fa-solid fa-crosshairs" }
});
centerItem.on("click", ({ map, lngLat }) => {
map.flyTo({ center: [lngLat.lng, lngLat.lat] });
});
menu.addItem(centerItem);
menu.addTo(map);When using the UMD build via a script tag, the library extends the mapboxgl global object:
<script>
const { MapboxContextMenu, ContextMenuItem } = mapboxgl;
const menu = new MapboxContextMenu({ theme: "auto", width: 180 });
const centerItem = new ContextMenuItem({
label: "Center map here",
start: { className: "fa-solid fa-crosshairs" }
});
centerItem.on("click", ({ map, lngLat }) => {
map.flyTo({ center: [lngLat.lng, lngLat.lat] });
});
menu.addItem(centerItem);
menu.addTo(map);
</script>For complete API documentation, see the API Reference.
The main context menu class for Mapbox GL JS and MapLibre GL JS maps.
const menu = new MapboxContextMenu(options);Options:
theme- theme to use:'light','dark', or'auto'(follows system preference). Defaults to'auto'.width- menu width as a CSS value (e.g.,'200px') or number in pixels.className- custom CSS class name for the menu element.
Methods:
addItem(item)- add a menu item.insertItem(index, item)- insert a menu item at a specific index.removeItem(item)- remove a menu item.addTo(map, target?)- add the menu to a map. Optionally restrict to specific layer(s). See Layer Targeting.remove()- remove the menu from the map.
Events:
Fired when the context menu is displayed.
Type: ContextMenuEvent
menu.on("show", (e) => {
console.log(`Menu opened at ${e.lngLat.lng}, ${e.lngLat.lat}`);
});Fired when the context menu is closed.
Type: ContextMenuEvent
menu.on("hide", (e) => {
console.log("Menu closed");
});A clickable menu item with optional content slots.
const item = new ContextMenuItem({
label: "Copy coordinates",
start: { className: "fa-solid fa-copy" }
});
item.on("click", ({ lngLat, map, point, features }) => {
// Handle click
});Options:
label- a text label to display.start- content to display before the label. See Slot Content.end- content to display after the label. See Slot Content.disabled- whether the item is disabled. Defaults tofalse.className- custom CSS class for the<li>element.buttonClassName- custom CSS class for the<button>element.
Properties:
label- get/set the label text.start- get/set the start slot content.end- get/set the end slot content.disabled- get/set the disabled state.
Events:
Fired when the menu item is clicked.
Type: ContextMenuItemEvent
item.on("click", (e) => {
console.log(`Clicked at ${e.lngLat.lng}, ${e.lngLat.lat}`);
});A menu item that displays a nested submenu on hover or click.
const submenu = new ContextMenuSubmenu({
label: "More options",
start: { className: "fa-solid fa-ellipsis" }
});
submenu.addItem(new ContextMenuItem({ label: "Option A" }));
submenu.addItem(new ContextMenuItem({ label: "Option B" }));Options:
- All
ContextMenuItemoptions, plus: showDelay- delay in ms before showing the submenu on hover. Defaults to300.hideDelay- delay in ms before hiding the submenu when mouse leaves. Defaults to200.
Methods:
addItem(item)- add an item to the submenu.insertItem(index, item)- insert an item at a specific index.removeItem(item)- remove an item from the submenu.
A non-interactive text label for grouping menu items into sections.
menu.addItem(new ContextMenuLabel({ text: "Navigation" }));
menu.addItem(new ContextMenuItem({ label: "Center map here" }));
menu.addItem(new ContextMenuItem({ label: "Zoom in" }));Options:
text- the text to display.className- custom CSS class for the label element.
A horizontal line for visually grouping menu items.
menu.addItem(new ContextMenuItem({ label: "Edit" }));
menu.addItem(new ContextMenuSeparator());
menu.addItem(new ContextMenuItem({ label: "Delete" }));Options:
className- custom CSS class for the separator element.
Fired by MapboxContextMenu.
| Property | Type | Description |
|---|---|---|
type |
"show" | "hide" |
The event type. |
target |
MapboxContextMenu |
The context menu that fired the event. |
map |
Map |
The Mapbox GL or MapLibre GL map instance. |
lngLat |
{ lng: number, lat: number } |
Geographic coordinates of the original right-click. |
point |
{ x: number, y: number } |
Pixel coordinates relative to the map container. |
features |
Feature[] |
Features at the click location (when menu is layer-scoped). |
originalEvent |
MouseEvent |
The original DOM event. |
Fired by ContextMenuItem.
| Property | Type | Description |
|---|---|---|
type |
"click" |
The event type. |
target |
ContextMenuItem |
The menu item that fired the event. |
map |
Map |
The Mapbox GL or MapLibre GL map instance. |
lngLat |
{ lng: number, lat: number } |
Geographic coordinates of the original right-click. |
point |
{ x: number, y: number } |
Pixel coordinates relative to the map container. |
features |
Feature[] |
Features at the click location (when menu is layer-scoped). |
originalEvent |
MouseEvent |
The original DOM click event. |
The start and end slots accept three types of content:
Rendered as text content:
new ContextMenuItem({
label: "Rating",
end: "★★★"
});For full control, pass a DOM element directly:
const icon = document.createElement("i");
icon.className = "fa-solid fa-star";
new ContextMenuItem({
label: "Favorite",
start: icon
});Create elements declaratively:
new ContextMenuItem({
label: "Favorite",
start: { className: "fa-solid fa-star" }
});Object notation supports:
as- element type to create. Defaults to'span'.className- CSS class name(s) to apply.content- text content for the element.onClick- click event handler.
new ContextMenuItem({
label: "Settings",
start: { as: "i", className: "fa-solid fa-gear" },
end: { content: "Beta", className: "badge" }
});Context menus can be scoped to specific map layers, so they only appear when the contextmenu event is triggered on features in those layers.
Pass a layer ID or array of layer IDs to addTo():
// Single layer
menu.addTo(map, "building");
// Multiple layers
menu.addTo(map, ["building", "building-outline"]);When the menu is triggered, the features property in the click event will contain one or more features at that location:
item.on("click", ({ features }) => {
if (features && features.length > 0) {
console.log(features[0].properties);
}
});Mapbox GL JS v3.9.0 introduced expanded targeting options, through the Interactions API, that allow you to target featuresets in imported basemaps like Mapbox Standard:
menu.addTo(map, { featuresetId: "buildings", importId: "basemap" });You can also target layers using this notation:
menu.addTo(map, { layerId: "my-custom-layer" });The library automatically detects whether these options are available and falls back to the traditional layer-based approach for older versions or MapLibre GL JS.
The menu supports full keyboard navigation:
- Arrow down/up - move focus between items
- Arrow right - open a submenu when the submenu item is focused
- Arrow left - close submenu and return to parent
- Enter/space - activate the focused item
- Escape - close the menu
The menu supports light and dark themes via the theme option. Use 'auto' to follow the user's system preference.
Custom styling can be applied via the className options on each component, or by overriding the CSS custom properties:
| Variable | Description | Light Default | Dark Default |
|---|---|---|---|
--context-menu-bg |
Menu background color | white |
#141414 |
--context-menu-font-family |
Menu font family | Inherited from map | Inherited from map |
--context-menu-border-radius |
Menu border radius | 5px |
5px |
--context-menu-min-width |
Menu minimum width | 200px |
200px |
--context-menu-item-text-color |
Item text color | black |
white |
--context-menu-item-font-size |
Item font size | 13px |
13px |
--context-menu-item-focus-bg |
Focused item background | #e8e8e8 |
#444444 |
--context-menu-item-active-bg |
Active item background | #f3f3f3 |
#2a2a2a |
--context-menu-item-disabled-opacity |
Disabled item opacity | 0.5 |
0.5 |
--context-menu-button-height |
Button height | 30px |
30px |
--context-menu-button-radius |
Button border radius | 2.5px |
2.5px |
--context-menu-separator-color |
Separator line color | #e8e8e8 |
#505050 |
MIT
