dash-leaflet2 β Leaflet 2 maps for Dash
Leaflet 2-native mapping components for Plotly Dash 4.
No react-leaflet Β· unified Pointer Events Β· BlanketOverlay canvas/WebGL layers Β· ES6-class subclassing Β· ResizeObserver sizing Β· map rotation Β· liquid-glass theme Β· full Dash callback interoperability.
Documentation Β· Discord Β· YouTube Β· GitHub
Live at leaflet.2plot.dev β every map on the docs site is a running Dash app.
Maintained by Pip Install Python LLC.
dash-leaflet is frozen on react-leaflet β Leaflet 1.9.
react-leaflet has no Leaflet 2 line, and its architecture is built around Leaflet 1.x's
context and lifecycle model β so a Leaflet-2-native component cannot ride react-leaflet.
dash-leaflet2 wraps Leaflet 2 core directly through a small React-context bridge.
That sheds the entire react-leaflet abstraction layer and unlocks v2's headline features:
- Unified Pointer Events β one event model for mouse, touch and stylus. The Leaflet
event's
.originalEventis a nativePointerEvent, sopointerType,pressureandtiltX/tiltYreach your Python callbacks BlanketOverlaycanvas / WebGL layers β draw your own renderer across the whole viewport instead of fighting the DOM layer system- ES6-class subclassing β extend Leaflet 2 classes in plain JS and mount the result as a Dash component
ResizeObserversizing β no more grey tiles when a map is born inside a hidden tab, accordion or drawer- Map rotation β
bearingas a first-class, two-way prop, with a keyboard control - Liquid-glass theme β tooltips, popups, the zoom bar and attribution use
color-mix()+backdrop-filter, so they read correctly in light and dark mode
The package ships the compiled JS bundle β Leaflet 2, the marker images and the theme CSS
all live inside dash_leaflet2.js. A normal pip install needs no Node and no
external_scripts.
Status: alpha. This tracks Leaflet
2.0.0-alpha.1, so the0.xline is itself alpha and APIs will move until v2 leaves alpha upstream. See CHANGELOG.md for what's shipped, fixed and known-broken.
Drone tracking, image overlays and map packages, built with this library.
pip install dash-leaflet2Requires dash>=4.1. See Dash compatibility for the tested matrix.
from dash import Dash, Input, Output, callback, html
import dash_leaflet2 as dl2
app = Dash(__name__)
app.layout = html.Div([
dl2.Map(
id="map",
center=[28.0206, -97.0544],
zoom=12,
style={"height": "70vh"},
children=[
dl2.TileLayer(url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"),
dl2.Marker(
id="pin",
position=[28.0206, -97.0544],
draggable=True,
children=dl2.Tooltip("Drag me"),
),
],
),
html.Pre(id="out"),
])
@callback(Output("out", "children"), Input("pin", "position"))
def show(position):
return f"marker at {position}"
if __name__ == "__main__":
app.run(debug=True)Drag the pin and position round-trips to Python. No JS, no external_scripts, no
Leaflet CSS link β the bundle carries all of it.
Full documentation, with a live interactive demo on every page:
π leaflet.2plot.dev
Every page also serves /<page>/llms.txt β the prose plus the complete example source,
directive-expanded and ready to paste into a chat window.
You can also run the docs site locally:
pip install -r requirements.txt
# markdown2dash pins gunicorn<22, against the CVE-driven gunicorn>=23 floor
# in requirements.txt. Its real dependencies are listed there, so it installs
# without its dependency graph β the docs pages need it.
pip install --no-deps markdown2dash==0.1.2
python run.py # open http://127.0.0.1:8050β¦or the standalone component harness, which exercises the compiled dl2.* surface
without the docs shell:
python usage.py # open http://127.0.0.1:806026 components, all loading from the single bundled JS file.
| Component | What it is |
|---|---|
Map |
The root container. Owns the Leaflet 2 map instance and provides it to children through React context. Two-way center / zoom / bearing / bounds, plus the full pro-prop surface (minZoom, maxBounds, zoomSnap, inertia, β¦). |
TileLayer |
Raster tile basemap. minZoom, bounds, errorTileUrl, zIndex, subdomains, detectRetina, tms, crossOrigin; opacity and zIndex are [MUTABLE]. |
Marker |
Icon at a position, hosting Popup / Tooltip children. Icon modes: default pin, custom image, emoji, or any Iconify icon. Draggable markers write position back to Dash. |
Popup / Tooltip |
Balloon and hover label bound to a parent layer. Children render through a React portal, so any Dash component works as content. |
| Component | What it is |
|---|---|
GeoJSON |
Renders a GeoJSON object from a callback. cluster=True collapses dense point sets via SuperCluster; custom pointToLayer / clusterToLayer JS strings supported. |
LayerGroup |
Bundles N layers so they add and remove together. |
FeatureGroup |
Like LayerGroup, but emits a combined GeoJSON of its vector children and broadcasts one click regardless of which child was hit. |
ImageOverlay |
Drapes a static image over a bounding box. With editable, gains click-to-select, drag-to-move, corner-resize and rotate handles; bounds / rotation / selected round-trip. |
| Component | What it is |
|---|---|
Circle |
Radius in meters β grows and shrinks with zoom. |
CircleMarker |
Radius in pixels β constant size at every zoom. |
Polyline |
Multi-segment line from [lat, lng] points. |
Polygon |
Filled closed shape from [lat, lng] points. |
Rectangle |
Axis-aligned box from geographic bounds. |
| Component | What it is |
|---|---|
LayersControl + BaseLayer / Overlay |
Base-layer radio group and overlay checkboxes. A custom implementation β Leaflet 2's bundled Layers class is not exported by its ESM. |
EditControl |
Native v2 draw/edit toolbar (leaflet-draw is Leaflet-1-only and DOA on v2). Marker / polyline / polygon / rectangle / circle / delete, round-tripping geojson, n_drawn and lastAction. |
AttributionControl |
Explicit attribution box. position and prefix are both [MUTABLE]; prefix=False hides the Leaflet link. |
ScaleControl |
Metric and/or imperial scale bar. |
FullScreenControl |
Toggles the map container through the browser's native requestFullscreen(). Leaflet 2 ships no fullscreen control. |
MiniMap |
Corner overview map tracking the main map's center and zoom, with a viewport rectangle and a collapse toggle. |
EasyButton |
Single-icon control for a map-level action; the click arrives as n_clicks. Icons from Iconify. |
KeyboardControl |
Window-level key listener driving rotation and pan. Renders no DOM β a pure side-effect component. |
TileSelector |
Turns the map into a tile picker: click or shift-drag to select tiles, which round-trip as {z, x, y, url, bounds}. |
TextMarker |
Editable, draggable, styleable text on the map. Double-click to edit; on-canvas resize / rotate handles and a contextual toolbar when selected. |
Full prop tables are generated into each dash_leaflet2/<Component>.py docstring by
dash-generate-components, and rendered on every documentation page.
- Two-way props are marked in the docstrings.
[MUTABLE]means Python callbacks can push the value into the map;[READONLY]means the map writes it back.Map.center,Map.zoom,Map.bearing,Marker.position,TileSelector.selectedTiles,ImageOverlay.boundsandEditControl.geojsonall round-trip in both directions. - Events are props, not a separate channel. Clicks land as
n_clicks, drags as an updatedposition, draws asgeojson+lastActionβ so a plain@callbackis the whole integration. - Pointer events carry v2's extra data. Pointer props report
pointerType,pressureandtiltX/tiltYfrom the nativePointerEvent, which Leaflet 1 could not express. - Only JSON crosses the boundary. Tiles, canvas bitmaps and the Leaflet instances themselves stay in the browser.
dash-leaflet2 targets Dash 4.1 and up. That range is verified, not assumed β
scripts/compat_matrix.py builds a throwaway virtualenv per Dash version, installs the
full documentation site into each, and runs the smoke suite there:
python scripts/compat_matrix.py # 4.1.0, 4.2.0, 4.3.0, 4.4.1
python scripts/compat_matrix.py 4.4.1 --backends flask fastapi
python scripts/compat_matrix.py --browser # + Playwright console-error sweepResults land in COMPATIBILITY.md. The per-version harness is
scripts/smoke_test.py, which also runs standalone:
python scripts/smoke_test.pyIt checks that every markdown page registered a route with no duplicate paths, that every
page layout builds and survives Dash's JSON encoder, and that every route plus
/_dash-layout, /_dash-dependencies, /healthz, /llms.txt, /robots.txt and
/sitemap.xml answers over the backend's test client. No socket, no browser.
# Install dependencies
npm install # TypeScript + webpack toolchain
pip install -r requirements.txt
# Build the JS bundle + regenerate the Python wrappers
npm run build # webpack UMD bundle + dash-generate-components
npm run build:js # webpack only (after .tsx edits)
npm run build:backends # regenerate Python classes only (after prop changes)
npm run watch # JS-only rebuild loop
# Run
python run.py # documentation site β http://127.0.0.1:8050
python usage.py # component harness β http://127.0.0.1:8060
# Test
python scripts/smoke_test.py
# Build a distribution
python -m build --wheel- TypeScript source of truth is
src/ts/βcomponents/holds the public components;icons.ts,theme.css,useLayer.tsandlayersControl-shared.tsare the shared plumbing;types/leaflet.d.tsis our ambient declaration (Leaflet 2 ships no types). - After editing
src/ts/components/*.tsxyou mustnpm run buildβ the Python classes and the JS bundle are generated artifacts. - The built bundle and generated wrappers are committed, so
pip install -e .works without npm. dash_leaflet2/__init__.pyis hand-maintained (it registers_js_dist);dash-generate-componentsdoes not regenerate it.- Defaults go in destructured default parameters, never
Component.defaultProps(React 18.3 deprecation warning;react-docgen@5reads both, so the defaults survive). - Keep the version in sync across
package.json,pyproject.tomlandlib/constants.pywhen cutting a release.
Each page is a docs/<slug>/<slug>.md with frontmatter plus a docs/<slug>/example.py
exporting a component. pages/markdown.py walks them and registers each as a Dash page;
.. exec:: embeds the live demo, .. source:: renders the example source, and
.. kwargs:: generates the prop table. Adding a page is two files and no routing code.
The deployed site at leaflet.2plot.dev also runs as a
2plot network satellite β ad slots, traffic rollups, Clerk sign-in and
an admin page-visibility board. Every one of those is dormant without its environment
keys, so a local python run.py is just the docs. See DEPLOYMENT.md.
- Python >= 3.9 (the documentation site itself needs >= 3.10 β see below)
- Dash >= 4.1
- Node.js >= 16 β only to rebuild the JS bundle
The package needs only Python 3.9+ and Dash 4.1+; every combination in
that range is verified in CI. Running the documentation site from source
additionally needs Python 3.10+, because python-frontmatter imports
typing.TypeGuard. That floor does not apply to pip install dash-leaflet2.
Leaflet 2 is a genuine break from 1.x, and these cost real debugging time:
- The version string is
2.0.0-alpha.1, with the dot. The dotless form 404s on unpkg. - The UMD build exposes
window.leaflet, notwindow.Lβ theLglobal is gone. - There are no lowercase factory functions. Use
new Marker(...), notL.marker(...). - v2 fires pointer events (
pointermove/pointerdown), notmousemove/mousedown. - Canvas-clear bug:
BlanketOverlay._onMoveEnd()draws and then calls_resizeContainer(), which setscanvas.widthand wipes the bitmap β so the Canvas renderer goes blank after pan/zoom. We re-issue a redraw on the next animation frame aftermoveend/zoomend. The SVG renderer is unaffected. - Default marker icons need explicit wiring when bundled: v2's CSS-based icon-path
detection fails, and
Marker's default icon is a shared instance created at module-load time. All of it is handled insrc/ts/icons.ts.
The documentation site covers each of these with a live page.
Come build with us:
- π¬ Discord β discord.gg/WEnZR35mrK
βΆοΈ YouTube β @2plotai- π Issues β github.com/pip-install-python/dash-leaflet2/issues
dash-leaflet2 is one of several tools built and maintained by Pip Install Python LLC:
| Project | What it is |
|---|---|
| π Pip Install Python | Open-source documentation index for the Python & Dash ecosystem |
| π 2plot.ai | Frame-accurate NLE timeline & scene compositor for Dash |
| ποΈ PiratesBargain.com | E-commerce / digital commerce |
| π§ ai-agent.buzz | Infinite AI canvas |
| π¬ 2plot.media | Videography application |
MIT β see LICENSE. Built by Pip Install Python LLC to bring a generation-ahead mapping stack into the Dash framework.