The first beta of 2.0 and the first release since 2021: a full modernization of the plugin. It now works with both Leaflet 2.0 and 1.9 from one package, is an ES module with its own TypeScript types, and uses only public Leaflet APIs where possible. Loading markers is 2× faster with over a third less memory, cluster animations are smoother and no longer depend on CSS, clusters are accessible to keyboard and screen reader users, and over 20 bugs are fixed.
The release targets Baseline 2022 browsers and Leaflet 1.9+. Breaking changes are listed after the improvements. Install it with npm install leaflet.markercluster@next, and please report any problems in #1115.
❇️ New features
- Support Leaflet 2.0 alongside 1.9, from the same package and builds (#1115)
- Ship as an ES module:
import {MarkerClusterGroup} from 'leaflet.markercluster'(#874) - Add TypeScript types, generated from the source and checked in strict mode, with typed options and cluster events. Groups are generic over the member type, e.g.
MarkerClusterGroup<CircleMarker>(#982) - Animate clusters with the Web Animations API instead of CSS transitions. Markers stay at their real position during animations (no more
moveevents or temporary latlngs), a new zoom cancels running animations cleanly, and spider legs draw out and fade in again (broken since Leaflet 1.0) - Add the
animationDurationoption (default 300 ms) for all cluster and spider animations - Add
getParent(layer)to get the cluster a marker (or cluster) is directly in, replacingmarker.__parent - Let
clusterclickhandlers cancel the default zoom or spiderfy withe.originalEvent.preventDefault()(#1103 by @eriknikulski) - Ship minified builds with source maps (#864)
⚡️ Performance
- Add markers about 2× faster:
addLayersof 200k markers takes 415 ms instead of 900 ms, 50k 72 ms instead of 172 ms, and adding a filled group to the map gets the same speedup - Make
addLayersbefore the group is on the map (the common "add everything, then add the group" pattern) no longer quadratic: adding 200k markers and then the group takes 0.4 s instead of 21 s (thanks to @pmarrapese for spotting it in #988) - Use about 38% less memory per marker (600 → 370 bytes at 200k), with one listener per marker instead of three and no extra properties on markers
- Zoom in about 4× faster on large groups (230 → 60 ms over 12 zoom levels with 200k markers)
- Fix quadratic adding and removing when the map has a high
minZoom: removing half of 200k scattered markers atminZoom: 10takes 0.4 s instead of 23 s - Make
clearLayersup to 40% faster, and skiplayeradd/layerremoveevents in bulk calls when nobody listens - Avoid forced layouts during cluster animations
⚠️ Breaking changes
- Require Leaflet 1.9 or 2.0 and a Baseline 2022 browser. Internet Explorer and the old-browser CSS fallbacks are gone
- The package entry is now an ES module that exports
MarkerClusterGroupandMarkerCluster, and no longer setsL.MarkerClusterGroup,L.markerClusterGroupor other globals. Usenew MarkerClusterGroup(options) dist/leaflet.markercluster.jsis now the ES module build. For a plain<script>tag, usedist/leaflet.markercluster-global.js, which setsL.MarkerClusterGroupandL.MarkerCluster(andL.markerClusterGroupon Leaflet 1.x). The-srcbuild and thewindow.Leaflet.markerclusterglobal are gone- Remove
MarkerCluster.css, since animations no longer use CSS: drop its import or<link>(keepMarkerCluster.Default.css). CSS overrides of.leaflet-cluster-anim .leaflet-marker-icontransitions no longer affect the animations; useanimationDuration - Remove
marker.__parentand the other properties the plugin set on markers (_spiderLeg,_preSpiderfyLatlng,_backupLatlng,__dragStart). Usegroup.getParent(marker)orgroup.getVisibleParent(marker)instead DistanceGrid,QuickHullandMarkerClusterNonAnimatedare no longer public- Keep a marker in one group at a time: adding it to another group removes it from the first. Before, this broke both groups and threw (#1051, #1048)
- On Leaflet 2.0, subclass with
class extends MarkerClusterGroup(extend()andinitialize()still work on 1.9), and Enter or Space on a cluster fireclusterclickinstead ofclusterkeypress getLayer(id)returnsundefinedfor an unknown id likeLayerGroup(wasnull),getVisibleParentreturns a visibleCircleMarkeritself instead of its cluster, andMarkerCluster.unspiderfy()no longer takes arguments
♿️ Accessibility
- Give clusters an accessible name ("12 markers") on the focusable icon, also for custom icons, with the new
clusterLabeloption for translation (#1107, thanks to @tmiaa for #1044) - Make spiderfy a proper disclosure: clusters get
aria-expanded, spiderfied markers come right after their cluster in the tab order, and activating a spiderfied cluster again (by keyboard or mouse) closes it - Close the spider with Escape, moving focus back to the cluster if it was on one of its markers. On Leaflet 1.9 and 2.0.0-beta.1, a Leaflet bug blocks this once any popup has been opened; it's fixed in 2.0.0-beta.2
- Keep focus where it is after activating a cluster with the keyboard, and move it to the map instead of the page when a focused cluster disappears on zoom
- Show the coverage polygon on keyboard focus, not just on hover
- Respect
prefers-reduced-motion: markers and spider legs fade in place instead of moving
🐞 Bug fixes
- Fix markers disappearing after the map's zoom range changes, e.g. when switching to a base layer with a different
maxZoom(#869) - Fix a dragged marker being clustered and vanishing when the map zooms mid-drag. It now leaves its clusters during the drag and rejoins on drop (#808)
- Fix a dragged spiderfied marker snapping back to the cluster on drop
- Fix
clearLayersduring chunkedaddLayersnot stopping the rest of the chunks (#816 by @frenomus) - Fix an error when the group is removed from the map during chunked loading; the remaining markers are now added when it's added back
- Fix unclustered markers never showing with a fractional map
minZoom - Fix markers flying off when zooming in on a spiderfied cluster
- Fix stale markers left on the map after an animated zoom out (thanks to @KristjanESPERANTO)
- Fix a removed spiderfied marker staying at its spider position if removed during a zoom
- Fix moving a marker after
removeLayersadding it back to the group - Fix
removeLayerof a marker from another group breaking that group's clusters - Fix removals while the group is off the map:
getBounds()included removed markers, a marker removed and added back disappeared fromgetLayers(), andclearLayers()kept old markers - Fix
addLayerof the same marker twice before the group is on the map adding it twice - Fix
getBounds()returning the old bounds after removing all markers - Fix
addLayersnot firinglayeraddwhile the group is off the map - Fix
removeLayer(id)for clustered markers - Fix
animateAddingMarkersandrefreshClustersthrowing or misbehaving with aCircleMarker - Fix a hang when the map has no finite
minZoom; it throws an error now (#991 by @maranderan)
🔧 Workflow
- Run tests with Vitest in the browser on both Leaflet 1.9 and 2.0 instead of Karma and PhantomJS, with many more specs
- Build with Rolldown, lint with ESLint against Baseline 2022, and run CI and npm releases on GitHub Actions
- Add a benchmark harness for loading, zooming and memory (
npm run bench) - Update the demos to ES modules and Leaflet 2.0, and use the preferred OpenStreetMap tile URL (#1072 by @Firefishy)