Skip to content

Using SVG icons for Leaflet js markers

Final result

We’ll see how to use SVG elements as icons for Leaflet markers. This will allow us to have all of the advantages of inline SVG, namely:

  • Small size.
  • Customizability via CSS styling.

The most powerful advantage will come with CSS styling. We’ll see how to easily change colors, or style the same SVG icons in different ways.

Setting up the SVG icon

For this tutorial, we’ll use a very simple SVG icon.

<svg
  width="100"
  height="100"
  viewBox="0 0 100 100"
  version="1.1"
  preserveAspectRatio="none"
  xmlns="http://www.w3.org/2000/svg"
>
  <path d="M0 0 L50 100 L100 0 Z" fill="##7A8BE7"></path>
</svg>

This icon has a size of 100×100 pixels. We’ll scale it down to a more appropriate size of 24×40 pixels for a marker icon. To do so, we set the height and width attributes of the <svg>. For the scaling to make effect, the viewBox attribute must be set to the “original” bounds of the SVG element.

<svg
  width="24"
  height="40"
  viewBox="0 0 100 100"
  version="1.1"
  preserveAspectRatio="none"
  xmlns="http://www.w3.org/2000/svg"
>
  <path d="M0 0 L50 100 L100 0 Z" fill="##7A8BE7"></path>
</svg>

Setting up a Leaflet map

We will setup a simple map as follows:

<!-- index.html -->
<head>
  ...
  <link href="styles.css" rel="stylesheet" />
</head>
<body>
  <div id="map"></div>
  <script src="script.js"></script>
</body>
/* sytles.css */
#map {
  height: 400px;
  width: 400px;
}
// script.js
const map = L.map("map").setView([0, 0], 1);
L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  attribution:
    '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
}).addTo(map);

The corresponding map will look as shown below.

Leaflet base map

For a more detailed explanation to setup Leaflet, see their official quick start guide.

Creating a new Leaflet icon

Now, we’ll see how to use the SVG element of the first section as a Leaflet icon. While there’s an official tutorial on how to use custom icons, it’s inappropriate for our purposes because they use the Icon class. This class is implemented using <img> and it would embed the <svg> element inside it. While this isn’t inherently bad, we would lose the capability of modifying the SVG icon directly using CSS.

For this reason, we’ll use the DivIcon class instead. This will preserve the <svg> element as an inline HTML component which can be styled using CSS.

DivIcon represents a lightweight icon for markers that uses a simple <div> element instead of an image.

Leaflet docs

To use the svg element as an icon, we pass it to the html property when creating a new DivIcon.

const svgIcon = L.divIcon({
  html: `
<svg
  width="24"
  height="40"
  viewBox="0 0 100 100"
  version="1.1"
  preserveAspectRatio="none"
  xmlns="http://www.w3.org/2000/svg"
>
  <path d="M0 0 L50 100 L100 0 Z" fill="#7A8BE7"></path>
</svg>`,
  className: "",
  iconSize: [24, 40],
  iconAnchor: [12, 40],
});

See that we’re also setting the iconSize to the corresponding size of the SVG element. The iconAnchor is the position of the “tip” of the icon, relative to the top-left corner. In our case, that’s the bottom-mid position of the icon. The classNameproperty is set to an empty string to avoid Leaflet adding the new icon to the leaflet-div-icon class. This would add both a border and a white background to the icon, which we don’t want.

Once defined, we can add a marker to the map using our previously defined svgIcon.

L.marker([0, 0], { icon: svgIcon }).addTo(map);

The resulting map.

Leaflet marker with SVG icons

Customizing the SVG icon

While we’ve already managed to use an SVG as a marker icon, let’s take this one step further by styling it using CSS.

First, let’s define a new CSS rule associated to the svg-icon class.

/* styles.css */
.svg-icon path {
  fill: crimson;
}

This rule will change the fill color of all the <path> elements that belong to a parent element of the svg-icon class.

We will add this class to our SVG icon when defining it.

const svgIcon = L.divIcon({
  html: `
<svg
  width="24"
  height="40"
  viewBox="0 0 100 100"
  version="1.1"
  preserveAspectRatio="none"
  xmlns="http://www.w3.org/2000/svg"
>
  <path d="M0 0 L50 100 L100 0 Z" fill="#7A8BE7"></path>
</svg>`,
  className: "svg-icon",
  iconSize: [24, 40],
  iconAnchor: [12, 40],
});

Now, when we add a new marker using svgIcon, it will look like this on the map.

Leaflet marker with an user defined CSS class

Leaflet markers with custom colors

As a final application, let’s give markers different colors while using the same icon.

First, we’ll create some new CSS rules.

.green path {
  fill: darkgreen;
}

.blue path {
  fill: navy;
}

.golden path {
  fill: gold;
}

Next, let’s create some new markers, and save their references.

const greenMarker = L.marker([-50, 0], { icon: svgIcon }).addTo(map);
const blueMarker = L.marker([50, 50], { icon: svgIcon }).addTo(map);
const goldenMarker = L.marker([50, -50], { icon: svgIcon }).addTo(map);

Finally, we’ll add the corresponding CSS classes to the icon of each marker.

greenMarker._icon.classList.add("green");
blueMarker._icon.classList.add("blue");
goldenMarker._icon.classList.add("golden");

The resulting map.

Leaflet markers with custom icon colors

Using inline SVG elements as icons for Leaflet markers, gives us a lot of flexibility when it comes to styling. We covered a few applications in this tutorial.

Working examples: here, codepen. For some reason the OSM tiles are not shown on Firefox, they work fine on Chrome.

Published inProgramming
Subscribe
Notify of
guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Abc
Abc
2 years ago

For reference. With Leaflet 1.7.1:

Uncaught TypeError: greenMarker._icon is undefined
Hans Erik Hazelhorst
Hans Erik Hazelhorst
2 years ago

Hi
Very nice! How can I rotate the SVG (using the anchor as rotation point)?

Chris
Chris
2 years ago

Property ‘_icon’ does not exist on type ‘Marker<any>’.

Albert
Albert
1 year ago

Thanks for the great SVG tutorial!

As an avid Firefox user, the fix for the examples is that you need to add “https:” to the OSM tiles, http won’t work because Firefox has a strict no-mixed https/http policy:

L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  attribution:
    '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
}).addTo(map);