I have some text labels in Leaflet which I'm building from JSON data using L.divIcon. They're looking like this:

lbl_coord = feature.properties.coord;

lbl_text = L.divIcon({
        className: /*...*/,
        iconAnchor: [0,0],
        popupAnchor: /*...*/,
        html: "<b>" + feature.properties.name + "</b>"

  var label = L.marker(lbl_coord, {icon: lbl_text});

Well, this works but the performance is rather bad because each marker is rendered as individual DOM element. It becomes very slow on panning operations. I'd like to load 10 000 labels at some point and am struggling to make it use canvas. I couldn't get the Canvas Markers Plugin to work as I'm experiencing the same issue that is discussed here (The HTMLImageElement provided is in the 'broken' state). However, the workaround proposed there won't work for me (error: Layer is not defined). Besides, there is no license given, so I don't know if I might use this at all for my project. Here's another Plugin but this wouldn't work with divIcon functionality.

At this point, I am wondering why this functionality is not part of Leaflet core. I know >10 000 markers is a lot but performance is already quite bad with 100 markers. I hope somebody's got a solution.

  • What is the issue with using Canvas Markers Plugin? Your link here gives just source of the plugin. – TomazicM Mar 25 '19 at 20:32
  • I'm sorry – I've edited the link. It's equivalent to the error shown in the second image there: The HTMLImageElement provided is in the 'broken' state. – Moehrengulasch Mar 25 '19 at 20:39
  • Problem with Canvas Markers Plugin is because of divIcon icons. It requires regular icons that are based in images. I found a way of using dynamic (changeable text) svg images for icons. It works great, just initial load time is long (5 secs for 1000 images). If you are interested, I can publish it as an answer. – TomazicM Mar 26 '19 at 19:47
  • Thanks for your help, I would very much appreciate it. – Moehrengulasch Mar 27 '19 at 8:44

Leaflet Canvas-Markers plugin expects regular L.icon icons that are based on images. Trick to get text into images dynamically is using svg images, defined with text descriptor.

Code for example below is taken from Canvas-Markers demo. Text of each icon is serial number of icon. Loading of 10.000 icons lasts about 3 seconds. When map is zoomed out so that all icons are displayed, things get a bit slow.

var map = L.map('map').setView([59.9578,30.2987], 10);
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
  preferCanvas: true

var ciLayer = L.canvasIconLayer({}).addTo(map);

function svgText(txt) {
  return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="30"><text x="0" y="30" style="font-size: 14px;">' + txt + '</text></svg>';      

var icon;
var img;
var markers = [];

for (var i = 0; i < 10000; i++) {
  img = 'data:image/svg+xml,' + encodeURIComponent(svgText(i));
  icon = L.icon({
    iconUrl: img,
    iconSize: [40, 30],
    iconAnchor: [20, 15]
  var marker = L.marker([58.5578 + Math.random()*1.8, 29.0087 + Math.random()*3.6], {icon: icon}).bindPopup("I Am "+i);

| improve this answer | |
  • Interesting, thank you! I'll look into it next week to see how it works for me. When included on the map, is it possible to alter the text size (img size) on zoom or would it be then necessary to reload all the icons? – Moehrengulasch Mar 27 '19 at 18:56
  • Yes, icon size can be changed. For marker with index i you change it with marker[i].options.icon.options.iconSize = [newWidth, newHeight];. – TomazicM Mar 27 '19 at 21:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.