Releases: Leaflet/Leaflet
Development snapshot
dev Add permissions write to snapshot workflow (#9890)
v2.0.0-alpha.1
Changes
❇️ New Features
- Map: New export
LeafletMapas an alias forMapby @willfarrell in #9804 - Control.Layer: New option
collapseDelayby @fyyyyy in #9612
✨ Refactorings (⚠️ Breaking Changes)
- Fully converted all classes to ESM by @simon04 in #9677
- Use optional chaining with function calls by @simon04 in #9737
❌ Removed Features (⚠️ Breaking Changes)
- Drop aliased functions
addEventListener,removeEventListener,addOneTimeEventListener,fireEvent,hasEventListenerson Evented by @lukewarlow in #9781 - Drop aliased functions
addListener,removeListeneron DomEvent by @Falke-Design in #9834
🐞 Bugfixes
- Cleanup map.locate() by @jorri11 in #9746
- Popup width by @IvanSanchez in #9765
- Fix overlay shifting with new BlanketOverlay by @Falke-Design in #9822
For more information checkout the blog post: https://leafletjs.com/2025/05/18/leaflet-2.0.0-alpha.html
v2.0.0-alpha
⚡ Modernization of Leaflet
After two and a half years of hard work, we’re thrilled to announce the first alpha release of Leaflet 2.0!
This release marks a major modernization of the Leaflet codebase. We've dropped support for Internet Explorer, removed legacy methods and polyfills, adopted modern standards like Pointer Events, and now publish Leaflet as an ESM module. The global L is no longer part of the core package (though it’s still available in the bundled version leaflet-global.js for backward compatibility).
For more information checkout the blog post: https://leafletjs.com/2025/05/18/leaflet-2.0.0-alpha.html
What's New in Leaflet 2.0
- Targeting only evergreen browsers
- Switched from
MouseandTouchevents toPointerEvents - ESM support and tree shaking
- Rewritten using standardized ES6 classes
Migration
- Replace all factory methods with constructor calls:
L.marker(latlng)➜new Marker(latlng) - Change the
<script>tag tomodule:<script type='module'> - Replace usage of
Lwith explicit imports from the Leaflet package:import { Marker } from 'leaflet'- if you are not using a package manager like npm, you can use a
importmap: https://leafletjs.com/examples/quick-start/
- if you are not using a package manager like npm, you can use a
- To have global access to variables of a
module-script, assign them to thewindowobject (Not recommended):window.map = map - Consider Leaflet V1 Polyfill if you are using outdated plugins
Old implementation
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const map = L.map('map').setView([51.505, -0.09], 13);
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
</script>
New implementation
Module
<script type="importmap">
{
"imports": {
"leaflet": "https://unpkg.com/leaflet@2.0.0-alpha1/dist/leaflet.js"
}
}
</script>
<script type="module">
import L, {Map, TileLayer, Marker, Circle, Polygon, Popup} from 'leaflet';
const map = new Map('map').setView([51.505, -0.09], 13);
const tiles = new TileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
</script>
Global Script
<script src="https://unpkg.com/leaflet@2.0.0-alpha1/dist/leaflet-global.js"></script>
<script>
const map = new L.Map('map').setView([51.505, -0.09], 13);
const tiles = new L.TileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
</script>
Need Legacy Support?
Check out this polyfill package to help ease the transition for legacy apps: Leaflet V1 Polyfill
Changes
❇️ New Features
- Automatically attributes OSM if OSM tiles are used and no attribution is provided by @Jouwee in #9489
- Added
decodingoption to ImageOverlay by @IvanSanchez in #8650 - Added option for setting value of
aria-labelfor popup close button by @ShivangMishra in #8590 - Implement
BlanketOverlayas Renderer superclass by @IvanSanchez in #8611 - Support SSR by @Falke-Design in #9385
- Implemented
trackResizefor L.Popup by @IvanSanchez in #9605 - VideoOverlay: add
controlsoption by @simon04 in #9666 - Add
aria-keyshortcutsto map container by @chcederquist in #9688 - Expand layers control on Enter keydown by @larsgw in #8556
✨ Refactorings (⚠️ Breaking Changes)
- Change
<section>to<fieldset>in the layers control by @tmiaa in #7912 - Use ResizeObserver for map resizes by @IvanSanchez in #8612
- Remove usage of global L and safeguard against it by @mourner in #8536
- Replace
Util.bind()withFunction.bind()by @jonkoops in #8484 - Replace
Util.isArray()withArray.isArray()by @jonkoops in #8683 - Replace
Util.create()withObject.create()by @jonkoops in #8681 - Replace
Util.trim()withString.prototype.trim()by @jonkoops in #8682 - Prefer
Object.hasOwn()overObject.prototype.hasOwnProperty()by @jonkoops in #8684 - Use the
classListAPI for class manipulation methods inDomUtilby @jonkoops in #8685 - Replace
DomUtil.hasClass()withclassList.contains()by @jonkoops in #8727 - Replace
DomUtil.getClass()withclassList.valueby @jonkoops in #8728 - Remove
DomUtil.setClass()method by @jonkoops in #8729 - Replace
DomUtil.addClass()withclassList.add()by @jonkoops in #8731 - Replace
DomUtil.removeClass()withclassList.remove()by @jonkoops in #8732 - Replace
DomUtil.remove()withElement.remove()by @jonkoops in #8735 - Replace
DomUtil.empty()withElement.replaceChildren()by @jonkoops in #8736 - Hold element positions in WeakMap by @jonkoops in #8749
- Destructure
staticsandincludesfrom class properties by @jonkoops in #8823 - Enforce
.jsfile extension for module imports by @jessetane in #8837 - Move
callInitHooks()to prototype ofClassby @jonkoops in #8825 - Use
Object.setPrototypeOf()to extendClassby @jonkoops in #8824 - Use Pointer Events for
Map.Keyboardby @Falke-Design in #8758 - Convert
Classto a JavaScriptclassby @jonkoops in #8871 - Drop
__super__property from Class by @jonkoops in #8800 - Move initialization logic for sub-classes to
Classconstructor by @jonkoops in #8872 - Use Pointer Events for
Map.BoxZoomby @jonkoops in #8757 - Use Pointer Events for
Control.Layersby @Falke-Design in #8759 - Drop UMD and make ESM the default entrypoint by @jonkoops in #8826
- Replace animation frame polyfill with native API by @jonkoops in #8810
- Rename
TouchZoomhandler toPinchZoom(keep TouchZoom as alias for backward compatibility) by @Mahendra-006 in #9642 - Use nullish coalescing and optional chaining by @simon04 in #9661
- Use arrow callback and shorthand methods by @simon04 in #9659
- Replace
Util.getParamStringwithURL.searchParamsby @simon04 in #9654 - Convert
Bounds,LatLng,LatLngBounds,Point,Transformationto ES6 classes by @simon04 in #9655 - Use for...of by @simon04 in #9653
- Replace
Util.extendwith ES6 Object.assign or object spread by @simon04 in #9652 - Refactor to
PointerEventsby @Falke-Design in #9620 - Convert
CRSto ES6 classes by @simon04 in #9669 - Use SVG icon for layer control by @simon04 in #9665
- Optimize PNG files by @dilyanpalauzov in #8661
- Add shadow-icon as svg icon and add white circle to marker-icon by @Falke-Design in #8768
❌ Removed Features (⚠️ Breaking Changes)
- Drop legacy VML code & official support for IE7–8 by @mourner in #8196
- Remove the long-deprecated
L.Mixin.Eventsand_flatby @mourner in #8537 - Remove Internet Explorer from browser detection by @jonkoops in #8559
- Remove legacy CSS hacks and unused prefixes by @mourner in #8600
- Remove Microsoft-specific Pointer Events from browser detection by @jonkoops in #8603
- Remove Android from browser detection by @jonkoops in #8607
- Remove Edge from browser detection by @jonkoops in #8606
- Remove Opera from browser detection by @jonkoops in #8621
- Remove PhantomJS from browser detection by @jonkoops in #8622
- Remove
indexOf()function from Util by @jonkoops in ht...
v1.9.4
🐞 Bug fixes
- Fix tile gaps in Chromium-based browsers (#8891 by @IvanSanchez)
- Fix vector drifts when
zoomAnimationisfalseand zooming viaflyToor pinch (#8794 by @plainheart) - Ensure
toGeoJSON()still works with an empty array (#8737 by @Falke-Design) - Ensure
LineUtilandPolyUtilonly iterate over array values and not properties (#8840 by @Falke-Design) - Fix rounding errors in center calculation of
LineUtilandPolyUtilfor small layers (#8784 by @Falke-Design) - Prevent unwanted layer toggle while expanding the Layers control on mobile (#8910 by @Falke-Design)
- Fix an error when a focusing on a
Tooltip-boundFeatureGroupthat contains a layer without agetElementmethod (#8890 by @Falke-Design) - Fix
Tooltipis not showing when loaded dynamically while moving the map (#8672 by @theGOTOguy) - Fix
noMoveStartoption not applying tofitBounds(#8911 by @AbdullahSohail-SE) - Fix outlines showing up when interacting with the map on Safari 16.4+ (#8917 by @jonkoops)
v1.9.3
🙌 Accessibility
- Expand the layers control on Enter keydown (#8556 by @larsgw)
- Align the scale control's alpha transparency with the attribution control (#8547 by @Malvoz)
- Allow the scale control's text to overflow the container (#8548 by @Malvoz)
🐞 Bug fixes
- Remove internal usage of
Lglobal (#8536 by @mourner) - Fix intermittent wobble when using
setMaxBounds(map.getBounds())(#8534 by @rjackson) - Ensure that
latLngsToCoords()does not return references passed to it (#7344 by @marlo22) - Ensure
worldCopyJump()behavior is consistent when using a keyboard (#8562 by @Falke-Design) - Ensure correct target is used for the
popupopenevent (#8571 by @Belair34) - Prevent recursion when panning a
Popup(#8520 by @rjackson) - Support CSS
position: stickyfor map container (#8550 by @tmiaa)
v1.9.2
🐞 Bug fixes
⚠️ Drop ESM entrypoint from package because of numerous compatibility issues with plugins (importleaflet/dist/leaflet-src.esm.jsexplicitly instead to take advantage; ESM by default will come in v2) (#8493 by @jonkoops)- Fix a bug where tooltips could throw an error with canvas renderer (#8498 by @Falke-Design)
- Fix a bug with incorrect marker popup location when there are multiple markers registered to the same popup (#8523 by @raychanks).
🧪 Tests
- Fix unit tests suite stopping abruptly on Mac (#8478)
📝 Docs
- Fix
Boundsequalsparameters in docs (#8500 by @Falke-Design)
v1.9.1
v1.9.0
⚡ Note on future versions
The v1.9 release is setting the stage for the first major version bump of Leaflet since 2016! A lot has changed since then, and it's time for Leaflet to grow together with the web platform.
After this release, we are branching off the 1.x code and putting it in maintenance mode — reserving potential 1.x releases only for critical bugfixes. Although version 2.0 is still far away and will take some time to take shape, we plan to make the following changes:
- Dropping support for Internet Explorer.
This has been a long time coming, but now that Internet Explorer is officially end-of-life, it's time to say goodbye. Going forward, Leaflet will move to an evergreen strategy that targets browsers like Firefox, Chrome, Edge and Safari. - Embracing modern JavaScript.
To maintain backwards compatibility, Leaflet is written entirely in ES5, a version of JavaScript supported by legacy browsers. So we have not been able to make use of many great JavaScript features (e.g. standardized classes, instead having to rely on our own implementation). By adopting a more modern version of the ECMAScript standard, we can start working towards aligning Leaflet with what is expected from a modern JavaScript library. - Standardized modules.
When we released Leaflet v1, the landscape in the JavaScript world was very different and full of competing module standards such as CommonJS, AMD and UMD. Today, ECMAScript modules have become the clear way forward to unite the JavaScript ecosystem under one banner. Moving forward, Leaflet will only be distributed in a single standardized module system, greatly reducing complexity of our distributed code. - Removing the Leaflet global.
As a developer using Leaflet, the capital letterLis probably intimately familiar to you. This is the Leaflet global where all of Leaflet's functionality lives. To allow compiler tooling to better eliminate dead-code through a process called tree-shaking, we are removing this global variable. To preserve backwards compatibility with older plugins, we will provide a shim that can be imported manually that will restore this functionality.
v1.9.0 changelog
⚠️ Breaking Changes
- (This change has been reverted in v1.9.2) Expose ESM entrypoint with Leaflet global (#8329 by @jonkoops).
- Update
color-adjusttoprint-color-adjust(#8211 by @Malvoz)
❇️ API changes
- Add
contentandlatLngoptions toPopup/Tooltipconstructors (#7783 by @Falke-Design) - Extend
Boundsto have the same functions asLatLngBounds(#7882 by @Falke-Design)
✨ Improvements
- Update
getCenter()calculation and move it toPolyUtil/LineUtil(#7603 by @Falke-Design) - Remove border styles in overflowing popups (#8260 by @Malvoz)
- Fix "listener not found" warning when setting
maxBounds(#8168 by @mourner) - Remove "listener not found" warning (#8234 by @Falke-Design)
- Extend
Events.listensto search for specific function (#8161 by @Falke-Design) - Add
noMoveStartoption topanTo(#6685 by @Chivano) - Add
FeatureCollectionhandling togeometryToLayer(#8163 by @Falke-Design)
🙌 Accessibility
- Improve
Tooltipaccessibility (focus and voice over) (#8247 by @alekzvik) - Fix links in accessibility guide (#8198 by @Malvoz)
- Remove
role="presentation"from image tiles (#8172 by @Malvoz)
🐞 Bug fixes
- Fix invalid GeoJSON on unbalanced arrays (#7637 by @steff1986)
- Fix 2 step zooming while using mouse wheel scrolling (#8298 by @Falke-Design)
- Fix wrong assigned parameter while calling
map._moveoverrequestAnimFrame(#8328 by @AMDvsTMD) - Fix
_isClickDisabledto not throw no error if parent is removed from DOM (#8288 by @Falke-Design) - Fix
DomEvent.DoubleTapto ignore clicks on<label>s with aforattribute (#8227 by @IvanSanchez) - Fix calling
once()twice if same event is fired insideonce(#8190 by @Falke-Design) - Fix
map.getCenter()returning a mutable object (#8167 by @mourner) - Fix regression about popup close button modifying the URL (#8160 by @IvanSanchez)
- Fix
min/maxZoomwhen used in combination withdetectRetina(#7328 by @bozdoz)
📝 Docs
- Use preferred tile.openstreetmap.org URL (#8418 by @Firefishy)
- Use LocalStorage for dialog sessions (#8382 by @ChristopherWirtOfficial)
- Update anchor links for headers and in collapsed accordions (#7780 by @Falke-Design)
- Fix typo in reference-1.6.0.html (#8330 by @eltociear)
- Add pre-commit linting to CONTRIBUTING.md (#8299 by @Falke-Design)
- Ensure no borders on dialog iframe (#8296 by @Malvoz)
- Replace Mapbox with OpenStreetMap in tutorials and examples (#7818 by @Falke-Design)
- Remove DOCS-TODO.md (#8259 by @Malvoz)
- Better PosAnimation example (#7386 by @stell)
- Correct heading level in GeoJSON example (#8230 by @Malvoz)
- Update Overlay Tutorial (ImageOverlay, VideoOverlay, SVGOverlay) (#8090 by @KonstantinBiryukov)
- Change attribute
anchortodata-anchor(#8174 by @KnightJam1) - Fix bad markdown causing link to not work (#8156 by @freyfogle)
- A couple of site SEO fixes (#8229 by @Malvoz)
- Fix attribution flag 1px misalignment on some websites (#8170 by @mourner)
- Attribution flag now resizes with font-size changes (#8183 by @sumitsaurabh927)
- Add Dialog to website (#8177 by @Falke-Design and #8193, #8194 by @Malvoz)
🔧 Workflow
- Improve GitHub Workflows security (#8419 by @sashashura)
- Update development dependencies
- Replace deprecated
eslint-plugin-script-tags(#8331 by @jonkoops) - Use major version ranges for Github Actions (#8286 by @jonkoops)
- Configure YAML issue forms (#8246 by @Malvoz)
- Add FUNDING.yml (@mourner)
- Add pre-commit hook to fix linting issues (#8212 by @jonkoops)
- Remove Dependabot specific labels (#8199 by @jonkoops)
- Use shorter bundlemon names (#8195 by @mourner)
- Make sure integrity hashes are generated for the built version (@mourner)
🧪 Tests
- Added test cases for
map.latLngToLayerPointmethod (#8407 by @kreloaded) - Add test for
map.panTo(#8390 by @anurag-dhamala) - Add test for
map.containerPointToLatLngandmap.latLngToContainerPoint(#8384 by @abhi3315) - Add test for
Layer._addZoomLimit(#8037 by @zishiwu123) - Add tests for
Map(#8206 by @stephenspol) - Add test for
CircleMarker._containsPoint(#8340 by @gernhard1337) - Add missing handler tests (#8182 by @Falke-Design)
- Cover Rectangle with unit Tests (#8144 by @stephenspol)
v1.9.x updates:
We've since released:
v1.8.0
v1.8.0 is a culmination of 1.5 years of development, a huge release focused on bug fixes, major reliability and accessibility improvements, cleaning up legacy code, and numerous improvements to documentation, development workflow and release process. A culmination of hundreds of contributions, and a preparation for bigger changes to come. 🍃
I'm making this release just as an air raid alert is sounding outside, in Kyiv, warning about an imminent Russian air strike. This release is dedicated to Ukrainian fight for freedom and democracy against the Russian invasion 🇺🇦 (see how you can support Ukraine here).
From now on, releases will become much more frequent. Thanks to our amazing community for all your help and patience. ❤️🙏 Special thanks to @johnd0e who revived Leaflet development after long stagnation and made the biggest contributions, @Falke-Design for doing the bulk of the work organizing development and preparing the release, @Malvoz for his numerous accessibility contributions, and @jonkoops for help with workflow automations. ❤️
⚠️ Breaking Changes
- Improve reliability of
contextmenuevent simulation on mobile Safari by introducing a newTapHoldhandler, replacing legacyTap(#7026 by @johnd0e) - Reorganize
DivOverlay/Popup/TooltipAPIs (#7540 by @johnd0e)- Move
Popuprelated options fromDivOverlaytoPopup(#7778 by @Falke-Design) - Change
Tooltipclass fromleaflet-clickabletoleaflet-interactive(#7719 by @Falke-Design) Map.closeTooltipnow requires a layer as argument (#7533 by @johnd0e)
- Move
- Improve error / argument handling for event listeners (#7518 by @johnd0e)
- Improve reliability of touch events simulation on non-touch devices (
DomEvent.Pointer) (#7059, #7084, #7415 by @johnd0e) - Improve reliability of
dblclickevent simulation on touch devices (DomEvent.DoubleTap) (#7027 by @johnd0e) - Improve reliability of
disableClickPropagation(#7439 by @johnd0e) - Improve
MaphasLayer()andLayerGrouphasLayer()to require a layer as argument (#6999 by @johnd0e) - Fix
Class.includeto not overwriteoptions(#7756 by @johnd0e) - Fix
Class.extendto not modify source props object (#6766 by @johnd0e) - Improve
Browser.touchtouch devices detection (#7029 by @johnd0e) - Get rid of legacy Android hacks (#7022 by @johnd0e)
- Allow fonts to respect users' browser settings by making the
font-sizerelative to the map container. (You can change the font size onleaflet-containerto adjust it if needed.) (#7800 by @Chandu-4444)
❇️ API changes
- Make
DivOverlay/Tooltipinteractive(#7531, #7532 by @johnd0e) - Add
openOn,close,togglefunctions toDivOverlay(#6639 by @johnd0e) - Introduce
DomEvent.off(el)to remove all listeners (#7125 by @johnd0e) - Allow preventing round-off errors by passing
falsetoUtil.formatNum/toGeoJSON(#7100 by @johnd0e) - Add
autoPanOnFocustoMarker(#8042 by @IvanSanchez) - Add
referrerPolicytoTileLayer(#7945 by @natevw) - Add
playsInlinetoVideoOverlay(#7928 by @Falke-Design) - Add
getCentertoImageOverlay(#7848 by @Falke-Design) - Fire a
tileabortevent when aTileLayerload is cancelled (#6786 by @dstndstn) - Add
crossOrigintoIcon(#7298 by @syedmuhammadabid)
✨ Improvements
- Improve memory footprint by removing
will-changeCSS property on tile images (#7872 by @janjaap) - Improve reliability of icons path detection heuristics (#7092 by @johnd0e)
- Improve performance of adding tiled sources by avoiding excessive updates in
GridLayer.onAdd(#7570 by @johnd0e) - Improve handling of edge cases in
panInside(#7469 by @daverayment) - Minify marker icon SVG (#7600 by @rala72)
- Allow template keys with spaces in
TileLayerURL (#7216 by @lubojr) - Improve behavior of
Tooltipbound toImageOverlay(#7306 by @IvanSanchez) - Remove the gap between Popup tip and content dialog (#7920 by @Malvoz)
- Fire
mousemovethrough Canvas to map if it has no layers (#7809 by @johnd0e) - Add print styles to prevent printers from removing background-images in controls (#7851 by @Malvoz)
- Move attribution code from
LayertoControl.Attribution(#7764 by @johnd0e) - Refactor
vmlCreate()so that it does not expose closure toTypeError(#7279 by @darcyparker) - Improve reliability of
Control.Layersby not relying on Browserandroidandtouchproperties (#7057 by @johnd0e) - Improve reliability of
Tooltipby not relying on Browsertouchchecks (#7535 by @johnd0e) - Make
Browsermutable for easier automated testing (#7335 by @bozdoz) - Replace
divwithspaninControl.Layerscontainer to fix an HTML validation error (#7914 by @tmiaa) - Add a Ukrainian flag to default attribution 🇺🇦 (by @mourner in #8109)
🙌 Accessibility
- Increase default font sizes and decrease attribution transparency for improved legibility (#8057, by @mourner)
- Improve accessibility of popup close button (#7908, by @Malvoz)
- Auto pan to markers on focus by default for improved keyboard operability (#8042 by @IvanSanchez)
- Add accessibility section to plugins guide (#7277 by @Malvoz)
- Update
Markerto default torole="button"&alt="marker"for an improved screen reader experience (#7895 by @tmiaa) - Set
role="button"for appropriate semantics on the<a>layers control (#7850 by @Malvoz) - Generally enable outlines for keyboard users by not stripping
outlineon focus for keyboard events (#7259 by @jafin) - Enable outlines on
leaflet-containerfor keyboard users (#7996 by @Malvoz) - Multiple enhancements to popup's close button ([#7794](https://gi...
v1.8.0-beta.3
🐞 Bug fixes
- Fix vector drift when dragging and immediately zooming (by @manubb @johnd0e @mourner in #8103)
- Reduce the occurrence of glitches on rapid zoom (by @mourner in #8102)
- Fix
autoPanOnFocuson icons with noiconSize(by @Falke-Design in #8091)
✨ Improvements
🧪 Tests and workflow
- Add GitHub Actions dependency tracking with Dependabot (by @nathannaveen in #8104)
- Cover
DomEventwith unit tests (by @stephenspol in #8088)
(Skipped v1.8.0-beta.2 because of a publishing blunder)