Skip to content

Leaflet.js Performance — Optimizing Map Rendering

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Leaflet.js Performance. We cover key concepts, practical examples, and best practices to help you master this topic.

Leaflet.js performance optimization ensures smooth map interactions by reducing tile loading, minimizing DOM operations, and using efficient rendering for large datasets.

What You'll Learn

By the end of this guide, you will optimize tile loading with Caching and subdomains, use canvas markers for thousands of points, debounce pan and zoom events, lazy load off-screen data, and profile map performance with browser tools.

Canvas Markers

// Use L.circleMarker instead of L.marker for large datasets
var markers = [];
for (var i = 0; i < 10000; i++) {
    markers.push(L.circleMarker(
        [40 + Math.random() * 5, -100 + Math.random() * 10],
        { radius: 3, color: '#4ecdc4', fillOpacity: 0.6 }
    ));
}
var layerGroup = L.layerGroup(markers).addTo(map);

Lazy Loading

map.on('moveend', function() {
    var bounds = map.getBounds();
    var center = map.getCenter();

    fetch('/api/points?lat=' + center.lat + '&lng=' + center.lng +
          '&swLat=' + bounds.getSouthWest().lat + '&neLat=' + bounds.getNorthEast().lat)
        .then(function(res) { return res.json(); })
        .then(function(data) {
            data.forEach(function(point) {
                L.circleMarker([point.lat, point.lng], { radius: 4 })
                    .addTo(map);
            });
        });
});

Debounced Events

var debounceTimer;
map.on('zoom', function() {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(function() {
        updateMarkerVisibility(map.getZoom());
    }, 200);
});

Common Mistakes

1. Using L.marker for Thousands of Points

L.marker creates DOM elements. For 5000+ points, use L.circleMarker or canvas-based rendering.

2. No Tile Layer Caching

Without caching, every pan reloads tiles. Use service workers or tile caching headers.

3. Fetching All Data on Load

Loading all GeoJSON data upfront blocks the main thread. Use server-side filtering based on map bounds.

4. Not Removing Off-Screen Layers

Layers outside the viewport still consume memory. Listen to 'moveend' and remove layers outside the extended bounds.

5. Expensive CSS on Map Elements

Complex CSS effects on map elements slow panning. Keep styles simple and avoid box-shadow on markers.

Practice Questions

Q1: What is the most performant way to display 10,000 points? A: Use L.circleMarker with canvas renderer or a canvas-based heatmap.

Q2: How do you reduce tile loading time? A: Use multiple subdomains, enable browser caching, pre-load tiles for adjacent zoom levels.

Q3: What event fires after the map stops moving? A: 'moveend' fires when both pan and zoom animations complete.

Q4: How do you lazy-load data based on map bounds? A: Listen to 'moveend', call an API with current bounds, and add returned layers.

Q5: What is the canvas renderer in Leaflet? A: L.canvas replaces SVG renderer. Use L.renderer.canvas for better performance with many layers.

Challenge: Build a map that displays 50,000 random points using L.circleMarker with canvas. Add zoom-based clustering that shows fewer points at low zoom levels.

FAQ

Does Leaflet use SVG or Canvas?

Leaflet defaults to SVG. Switch to canvas with L.canvas() for better performance with many layers.

How do I profile Leaflet performance?

Use Chrome DevTools Performance tab. Record a pan/zoom session and look for long frames.

What is the recommended maximum number of markers?

5000 for SVG, 50,000+ for canvas. Beyond that, use clustering.

How do I throttle tile loading?

Set updateWhenIdle: true on the tile layer to only load tiles when the map is idle.

Does Leaflet support Web Workers?

Not natively. Use workers for data processing and pass results to the main thread.

Try It Yourself

Build a map with 10,000 circle markers.

<!DOCTYPE html>
<html>
<head>
    <title>Performance Demo</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
</head>
<body style="font-family:sans-serif;padding:20px;">
<h2>10,000 Points</h2>
<div id="map" style="height:400px;border-radius:8px;"></div>
<script>
var map = L.map('map', {
    preferCanvas: true
}).setView([42, -95], 5);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; OSM',
    updateWhenIdle: true
}).addTo(map);

var markers = [];
for (var i = 0; i < 10000; i++) {
    markers.push(
        L.circleMarker(
            [35 + Math.random() * 14, -105 + Math.random() * 20],
            {
                radius: 2 + Math.random() * 3,
                color: '#4ecdc4',
                fillOpacity: 0.5 + Math.random() * 0.3,
                weight: 0
            }
        )
    );
}

var group = L.layerGroup(markers).addTo(map);
</script>
</body>
</html>

What's Next

Add map animations.

Animations — Map animations. Project — Complete map project.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro