Custom Marker Icons with MapLibre GL JS
MapLibre GL JSUse custom HTML elements or symbol layers with image sprites for marker icons.
const map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
center: [14.42, 50.08],
zoom: 13
});
const makeSvgEl = (color) => {
const el = document.createElement('div');
el.innerHTML = `<svg width="30" height="40" viewBox="0 0 30 40" xmlns="http://www.w3.org/2000/svg"><path d="M15 0C6.7 0 0 6.7 0 15c0 11.3 15 25 15 25s15-13.7 15-25C30 6.7 23.3 0 15 0z" fill="${color}"/><circle cx="15" cy="14" r="6" fill="#fff"/></svg>`;
el.style.cursor = 'pointer';
return el;
};
new maplibregl.Marker({ element: makeSvgEl('#e63946') })
.setLngLat([14.4205, 50.0875])
.setPopup(new maplibregl.Popup({ offset: 25 }).setHTML('<b>Old Town Square</b>'))
.addTo(map);
const badgeEl = document.createElement('div');
badgeEl.style.cssText = 'background:#2196F3;color:#fff;border-radius:50%;width:36px;height:36px;display:flex;align-items:center;justify-content:center;font-weight:bold;font-size:14px;border:2px solid #fff;cursor:pointer;';
badgeEl.textContent = 'C';
new maplibregl.Marker({ element: badgeEl })
.setLngLat([14.4006, 50.0904])
.setPopup(new maplibregl.Popup({ offset: 25 }).setHTML('<b>Prague Castle</b>'))
.addTo(map);How it works
MapLibre DOM markers accept a custom HTML element via the element option. You can use any DOM node: an inline SVG, an image, or a styled div. The element replaces the default pin entirely, giving full control over appearance.
The helper function makeSvgEl() creates a pin-shaped SVG with a configurable fill color. The second marker uses a circular badge with a letter. Both support popups via .setPopup() just like default markers. For large numbers of custom icons, use a symbol layer with map.addImage() instead: that path renders on the GPU.
Key APIs
| API | Description | Docs |
|---|---|---|
maplibregl.Marker({ element }) |
Custom HTML element marker | maplibre.org docs |
maplibregl.Popup |
Info popup for markers | maplibre.org docs |
map.addImage() |
Register image for symbol layers | maplibre.org docs |
FAQ
When should I use DOM markers vs. symbol layers?
DOM markers are best for under ~200 points with rich HTML content. Symbol layers render on the GPU and handle thousands of icons efficiently, but only support raster images registered via map.addImage().
How do I anchor the custom element to the correct point?
By default the element’s center is placed at the marker coordinates. Use the anchor option (top, bottom, center) or CSS transform to adjust the anchor point.
Common mistakes
- Using
[lat, lng]instead of[lng, lat]: MapLibre uses longitude first - Not setting
cursor: pointeron the custom element: the marker looks non-interactive without it - Creating thousands of DOM markers: this causes scroll jank; switch to symbol layers for large datasets
Build maps using MapLibre GL JS with AI
Generate code like this custom marker icons with maplibre gl js example using the MapLibre GL JS skill in your IDE.
Related Examples
- Add Markers and Popups in MapLibre GL JS
- See how to do this in Leaflet, MapTiler SDK or OpenLayers