MapSnippets logoMapSnippets logo

Custom Marker Icons with MapLibre GL JS

MapLibre GL JS

Use custom HTML elements or symbol layers with image sprites for marker icons.

progress_activityLoading map...
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: pointer on 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
auto_awesome

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.