# leaflet-components
[Leaflet.js](https://leafletjs.com/) as native Web Components (Custom Elements). Each HTML element maps 1:1 to a Leaflet object with reactive attribute binding.
## Installation
```bash
npm install leaflet-components
```
## Import options
The package ships multiple bundle formats and supports deep imports for tree-shaking.
```js
// Default ESM bundle (recommended) — registers all components
import 'leaflet-components';
// Minified ESM bundle
import 'leaflet-components/dist/index.min.js';
// CommonJS
const lc = require('leaflet-components');
// Deep import — register only one component
import 'leaflet-components/dist/components/leaflet-marker.js';
// UMD (for script tags, expects Leaflet as window.L)
//
```
`leaflet` is always an external dependency — you must install it yourself.
## Usage
Import once to register all custom elements, then use them declaratively in HTML.
```html
Hello!
Hover me
I am a circle
```
## `leaflet-map`
The root component. All other components must be children of ``.
```html
```
### View state
These attributes stay in sync with the map as the user interacts with it — panning updates `lat`/`lng`, zooming updates `zoom`.
| Attribute | Default | Description |
|---|---|---|
| `lat` | `0` | Center latitude |
| `lng` | `0` | Center longitude |
| `zoom` | `2` | Zoom level |
| `min-zoom` | `0` | Minimum zoom level |
| `max-zoom` | — | Maximum zoom level. When unset, Leaflet uses the tile layer's own max zoom. |
### Interaction
Boolean options that default to `true` are controlled by the presence of a `disable-*` attribute. Handler-based options can be toggled at any time; the rest only apply at construction.
| Attribute | Controls | Live? |
|---|---|---|
| `disable-dragging` | Mouse/touch panning | ✓ |
| `disable-scroll-wheel-zoom` | Scroll-wheel zoom | ✓ |
| `disable-double-click-zoom` | Double-click zoom | ✓ |
| `disable-touch-zoom` | Pinch-to-zoom | ✓ |
| `disable-box-zoom` | Shift-drag zoom box | ✓ |
| `disable-keyboard` | Keyboard pan/zoom | ✓ |
| `disable-zoom-control` | Built-in zoom control | — |
| `disable-attribution-control` | Built-in attribution | — |
| `disable-close-popup-on-click` | Close popup on map click | — |
| `disable-track-resize` | Auto-resize on window resize | — |
| `disable-bounce-at-zoom-limits` | Bounce animation at min/max zoom | — |
| `disable-tap-hold` | Long-press context menu (mobile) | — |
Boolean options that default to `false` are enabled by adding the attribute:
| Attribute | Description |
|---|---|
| `prefer-canvas` | Render vector layers on Canvas instead of SVG |
| `world-copy-jump` | Pan to the original world copy when crossing the antimeridian |
### Animation
| Attribute | Default | Description |
|---|---|---|
| `disable-zoom-animation` | — | Disable CSS zoom animation |
| `disable-fade-animation` | — | Disable tile fade-in |
| `disable-marker-zoom-animation` | — | Disable marker zoom animation |
| `zoom-animation-threshold` | `4` | Max zoom delta for animated zoom |
### Inertia & panning
| Attribute | Default | Description |
|---|---|---|
| `disable-inertia` | — | Disable inertial panning |
| `inertia-deceleration` | `3000` | Deceleration rate (px/s²) |
| `inertia-max-speed` | `Infinity` | Maximum inertia speed (px/s) |
| `ease-linearity` | `0.2` | Pan easing linearity |
### Zoom behaviour
| Attribute | Default | Description |
|---|---|---|
| `zoom-snap` | `1` | Zoom snapping interval; `0` for continuous zoom |
| `zoom-delta` | `1` | Zoom step per keyboard/button press |
| `max-bounds-viscosity` | `0` | How much the bounds resist panning past them (`0`–`1`) |
### Scroll wheel
| Attribute | Default | Description |
|---|---|---|
| `wheel-debounce-time` | `40` | Debounce delay for wheel events (ms) |
| `wheel-px-per-zoom-level` | `60` | Pixels of scroll per zoom level |
### Keyboard & touch
| Attribute | Default | Description |
|---|---|---|
| `keyboard-pan-delta` | `80` | Pan distance per key press (px) |
| `tap-tolerance` | `15` | Max touch movement to trigger a tap (px) |
### Rendering
| Attribute | Default | Description |
|---|---|---|
| `transform-3d-limit` | `8388608` | Max CSS `translate3d` component value before a layer reset |
### Accessing the underlying map
```js
const el = document.querySelector('leaflet-map');
el.map; // L.Map instance (undefined before connected)
el.zoom; // current zoom (reads live from map, falls back to attribute)
el.scrollWheelZoom = false; // disable at runtime
```
All `leaflet-map` properties are two-way: reading returns the live map value; writing updates both the attribute and the map.
### CSS
Leaflet's stylesheet is loaded from a CDN via a `` in the shadow DOM. Marker icon paths are derived from the CSS URL automatically.
| Attribute | Default | Description |
|---|---|---|
| `css-url` | `https://unpkg.com/leaflet@1.9.4/dist/leaflet.css` | URL for Leaflet CSS |
| `css-integrity` | `sha256-...` | SRI hash (required with default URL; when `css-url` is custom, no default integrity is used unless explicitly set) |
| `css-crossorigin` | _(auto)_ | `"anonymous"` when integrity is active, otherwise absent. Set explicitly to override. |
### Resize
A `ResizeObserver` on the host element automatically calls `map.invalidateSize()` whenever the element's dimensions change — from CSS classes, inline styles, attribute changes, or parent layout.
---
## Components
### ``
| Attribute | Default | Description |
|---|---|---|
| `url` | `''` | Tile URL template (`https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`) |
| `attribution` | `''` | Attribution text |
| `min-zoom` | `0` | Minimum zoom level |
| `max-zoom` | `18` | Maximum zoom level |
| `opacity` | `1.0` | Tile layer opacity |
| `z-index` | `0` | Z-index |
### ``
| Attribute | Default | Description |
|---|---|---|
| `url` | `''` | WMS service URL |
| `layers` | `''` | Comma-separated layer names |
| `styles` | `''` | Comma-separated style names |
| `format` | `'image/jpeg'` | Image format |
| `transparent` | — | Request transparent tiles |
| `version` | `'1.1.1'` | WMS version |
| `uppercase` | — | Use uppercase WMS parameter names |
### ``
| Attribute | Default | Description |
|---|---|---|
| `lat` | `0` | Latitude |
| `lng` | `0` | Longitude |
| `title` | `''` | Tooltip text on hover |
| `alt` | `''` | Alt text for the marker image |
| `draggable` | — | Allow dragging the marker |
| `opacity` | `1.0` | Marker opacity |
| `z-index-offset` | `0` | Z-index offset |
### ``
All [path style](#path-style) attributes apply.
| Attribute | Default | Description |
|---|---|---|
| `lat` | `0` | Center latitude |
| `lng` | `0` | Center longitude |
| `radius` | `1000` | Radius in meters |
### ``
All [path style](#path-style) attributes apply.
| Attribute | Default | Description |
|---|---|---|
| `lat` | `0` | Center latitude |
| `lng` | `0` | Center longitude |
| `radius` | `10` | Radius in pixels |
### ``
Coordinates are taken from child `` elements, not from attributes. All [path style](#path-style) attributes apply (excluding `fill*` — polylines don't fill).
| Attribute | Default | Description |
|---|---|---|
| `color` | `'#3388ff'` | Stroke color |
| `weight` | `3` | Stroke width (px) |
| `opacity` | `1.0` | Stroke opacity |
### ``
Coordinates are taken from child `` elements, not from attributes. All [path style](#path-style) attributes apply.
### ``
All [path style](#path-style) attributes apply.
| Attribute | Default | Description |
|---|---|---|
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
### ``
Vertex helper — not rendered directly. Used as a child of `` or ``.
| Attribute | Default | Description |
|---|---|---|
| `lat` | `0` | Latitude |
| `lng` | `0` | Longitude |
### ``
| Attribute | Default | Description |
|---|---|---|
| `url` | `''` | Image URL |
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
| `opacity` | `1.0` | Overlay opacity |
| `alt` | `''` | Alt text |
| `interactive` | — | Receive mouse/touch events |
| `cross-origin` | `''` | CORS setting for the image |
| `error-overlay-url` | `''` | Fallback image on load error |
| `z-index` | `0` | Z-index |
| `class-name` | `''` | CSS class for the image element |
### ``
| Attribute | Default | Description |
|---|---|---|
| `url` | `''` | Video URL |
| `bounds` | `''` | Bounding box as JSON: `[[south,west],[north,east]]` |
| `opacity` | `1.0` | Overlay opacity |
| `alt` | `''` | Alt text |
| `interactive` | — | Receive mouse/touch events |
| `cross-origin` | `''` | CORS setting for the video |
| `loop` | — | Loop playback |
| `autoplay` | — | Start playing automatically |
| `muted` | — | Mute audio |
| `playsinline` | — | Play inline (mobile) |
### ``
Content comes from an inline `