Skip to content

Leaflet.js Marker Clustering — Grouping Points at Scale

DodaTech Updated 2026-06-28 3 min read

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

Leaflet.js marker clustering groups nearby markers into clusters that expand as users zoom in, enabling performant display of thousands of points.

What You'll Learn

By the end of this guide, you will install and configure Leaflet.markercluster, customize cluster appearance, set clustering distance and bounds, handle cluster click events, and render 10,000+ points smoothly.

Basic Clustering

// npm install leaflet.markercluster
var markers = L.markerClusterGroup();

for (var i = 0; i < 1000; i++) {
    var marker = L.marker([
        40 + Math.random() * 5,
        -100 + Math.random() * 10
    ]);
    markers.addLayer(marker);
}

map.addLayer(markers);

Custom Cluster Icons

var markers = L.markerClusterGroup({
    iconCreateFunction: function(cluster) {
        var count = cluster.getChildCount();
        var size = count < 10 ? 'small' : count < 100 ? 'medium' : 'large';
        return L.divIcon({
            html: '<span>' + count + '</span>',
            className: 'cluster cluster-' + size,
            iconSize: L.point(40, 40)
        });
    }
});

Spiderfier Configuration

var markers = L.markerClusterGroup({
    spiderfyOnMaxZoom: true,
    showCoverageOnHover: false,
    zoomToBoundsOnClick: true,
    maxClusterRadius: 80,
    disableClusteringAtZoom: 16
});

Common Mistakes

1. Not Importing CSS

markercluster requires its CSS file. Missing CSS causes invisible or malformed clusters.

2. Clustering Too Few Markers

If maxClusterRadius is too large, distant markers cluster together misleadingly.

3. Spiderfier on Dense Clusters

With disableClusteringAtZoom too high, thousands of markers render individually. Set it judiciously.

4. Not Using chunkedLoading

For 50,000+ markers, use chunkedLoading: true to add layers in batches via requestAnimationFrame.

5. Cluster Click Propagation

Clicking a cluster zooms by default. Stop propagation if you need custom click behavior.

Practice Questions

Q1: What library provides clustering for Leaflet? A: Leaflet.markercluster.

Q2: How does maxClusterRadius work? A: It sets the maximum distance in pixels for markers to be grouped into one cluster.

Q3: What is spiderfier? A: When zooming into a cluster at max zoom, markers expand into a spiral arrangement to avoid overlap.

Q4: How do you customize cluster icons? A: Use iconCreateFunction on the markerClusterGroup options.

Q5: How many markers can markercluster handle? A: Up to 100,000 with chunkedLoading. Beyond that, use canvas-based rendering.

Challenge: Build a map with 5,000 randomly placed markers. Add a search box that zooms to the cluster containing the matching marker.

FAQ

Does clustering work with CircleMarkers?

Yes. markercluster accepts any layer type, not just markers.

How do I preserve marker popups with clustering?

Bind popups to individual markers before adding them to the cluster group.

Can I animate clusters?

Yes. Use L.markerClusterGroup with animate: true (default) for smooth cluster expand/collapse.

How do I filter clustered markers?

Remove markers from the cluster group and re-add filtered ones.

Does clustering work on mobile?

Yes. markercluster handles touch events for cluster tap and expansion.

Try It Yourself

Build a map with random points and custom clusters.

<!DOCTYPE html>
<html>
<head>
    <title>Clustering Demo</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5/dist/MarkerCluster.css" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5/dist/MarkerCluster.Default.css" />
    <script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet.markercluster@1.5/dist/leaflet.markercluster.js"></script>
    <style>.cluster-small { background-color: #4ecdc4; border-radius: 50%; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: bold; } .cluster-medium { background-color: #ff6b35; border-radius: 50%; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: bold; } .cluster-large { background-color: #e74c3c; border-radius: 50%; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: bold; }</style>
</head>
<body style="font-family:sans-serif;padding:20px;">
<h2>Marker Clustering</h2>
<div id="map" style="height:400px;border-radius:8px;"></div>
<script>
var map = L.map('map').setView([42, -95], 5);

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

var markers = L.markerClusterGroup({
    iconCreateFunction: function(cluster) {
        var count = cluster.getChildCount();
        var size = count < 10 ? '30px' : count < 50 ? '40px' : '48px';
        var cls = count < 10 ? 'cluster-small' : count < 50 ? 'cluster-medium' : 'cluster-large';
        return L.divIcon({
            html: '<span>' + count + '</span>',
            className: cls,
            iconSize: L.point(size.replace('px',''), size.replace('px',''))
        });
    },
    maxClusterRadius: 60
});

for (var i = 0; i < 500; i++) {
    var lat = 35 + Math.random() * 14;
    var lng = -105 + Math.random() * 20;
    var marker = L.marker([lat, lng]);
    marker.bindPopup('Point ' + i + '<br>Lat: ' + lat.toFixed(3) + ', Lng: ' + lng.toFixed(3));
    markers.addLayer(marker);
}

map.addLayer(markers);
</script>
</body>
</html>

What's Next

Create heatmap layers.

Heatmap — Heatmap visualization. Routing — Routing and directions.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro