MapSnippets logoMapSnippets logo

Custom Marker Icons with Leaflet

Leaflet

Replace the default marker pin with custom PNG or SVG icons using L.icon() and L.divIcon().

progress_activityLoading map...
const map = L.map('map').setView([48.8566, 2.3522], 13);
L.tileLayer('https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', {
  tileSize: 256,
  attribution: '&copy; <a href="https://www.maptiler.com/">MapTiler</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);
const svgIcon = L.icon({
  iconUrl: 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 30 40"><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="#e63946"/><circle cx="15" cy="14" r="6" fill="#fff"/></svg>'),
  iconSize: [30, 40],
  iconAnchor: [15, 40],
  popupAnchor: [0, -40]
});
L.marker([48.8584, 2.2945], { icon: svgIcon }).addTo(map).bindPopup('Eiffel Tower — SVG icon');
const htmlIcon = L.divIcon({
  html: '<div style="background:#2196F3;color:#fff;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-weight:bold;border:2px solid #fff;">L</div>',
  iconSize: [32, 32],
  iconAnchor: [16, 16],
  className: ''
});
L.marker([48.8606, 2.3376], { icon: htmlIcon }).addTo(map).bindPopup('Louvre — HTML divIcon');
const greenPin = L.icon({
  iconUrl: 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 30 40"><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="#4CAF50"/><circle cx="15" cy="14" r="6" fill="#fff"/></svg>'),
  iconSize: [30, 40],
  iconAnchor: [15, 40],
  popupAnchor: [0, -40]
});
L.marker([48.8530, 2.3499], { icon: greenPin }).addTo(map).bindPopup('Notre-Dame — green SVG icon');

How it works

Leaflet provides two ways to customize marker icons. L.icon() accepts an image URL (including inline data URIs for SVG) along with size, anchor, and popup offset. L.divIcon() lets you use arbitrary HTML, which is perfect for badges, labels, or icon fonts.

The iconAnchor controls which pixel of the icon sits on the marker’s coordinates. For a pin shape, set it to the bottom center. The popupAnchor determines where the popup opens relative to the icon anchor. Setting className: '' on divIcon removes the default white-background class that Leaflet applies.

Key APIs

API Description Docs
L.icon() Create icon from image URL leafletjs.com/reference.html#icon
L.divIcon() Create icon from HTML content leafletjs.com/reference.html#divicon
iconAnchor Pixel that aligns with marker coordinates leafletjs.com/reference.html#icon-iconanchor

FAQ

How do I use a PNG file instead of an SVG data URI?

Replace the data URI with the PNG file path: iconUrl: '/images/my-pin.png'. Set iconSize to match the actual image dimensions.

Can I animate the custom icon?

Yes. With L.divIcon() you have full CSS control: add CSS animations or transitions to the HTML element inside the html option.

How do I handle retina displays?

Provide an iconRetinaUrl option pointing to a 2x resolution image alongside the standard iconUrl.

Common mistakes

  • Forgetting iconAnchor: the default is the top-left corner, so the pin appears shifted from its actual position
  • Not setting className: '' on L.divIcon(): Leaflet adds a default class with a white background and border
  • Using [lng, lat] instead of [lat, lng]: Leaflet markers always use latitude first
auto_awesome

Build maps using Leaflet with AI

Generate code like this custom marker icons with leaflet example using the Leaflet skill in your IDE.