---
title: Leaflet & OpenStreetMap
description: Set up Leaflet as the map engine with OpenStreetMap tiles — install, attribution, usage policy, license, and live previews
visibility: guest
draft: false
---

# Leaflet & OpenStreetMap

[Leaflet](https://leafletjs.com/) is the lightweight alternative engine behind
[`<x-wirekit::map>`](/components/map). It renders **raster** tiles (pre-rendered
image tiles from a provider), is tiny (~40 KB), and is distributed under the
**BSD-2-Clause** license. Like MapLibre, it is a peer dependency — you load it;
WireKit never bundles it.

Its classic pairing is [OpenStreetMap](https://www.openstreetmap.org/) (OSM): a
free, crowd-sourced map of the world. OSM is **not a JavaScript library** — it
is a **tile source** (and the underlying map data) that an engine renders. The
two are different layers of the same map: Leaflet draws, OSM supplies the
imagery. That's why this page covers them together — install the engine first,
then point it at the tiles.

::: info
This guide covers the Leaflet engine and the OpenStreetMap tile source. For the
marker model, the accessible list, the props, and Livewire wiring — all
engine-independent — see the [Map overview](/components/map). OSM gives you
free, detailed, worldwide street tiles with no API key — ideal for prototypes
and low-traffic sites; for production scale, read **OSM Usage Policy & Cost**
below before you ship.
:::

## Install

```bash
# 1. Install Leaflet (a peer dependency — not bundled by WireKit).
npm install leaflet
```

Then load the engine in your layout — order matters, WireKit's glue looks for
it at Alpine init:

```blade
<!-- 2. Load Leaflet's JS + CSS BEFORE @wirekitScripts so window.L exists
     when WireKit's map glue initializes. -->
<link href="https://unpkg.com/leaflet/dist/leaflet.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
@wirekitScripts
```

Then set `provider="leaflet"` on the component. Once `window.L` is present, the
map upgrades from the marker-list fallback to a live Leaflet canvas.

## Tiles & Attribution

Leaflet needs a **raster tile source** — point `styleUrl` at a tile-URL template
(the `{z}/{x}/{y}` placeholders are the zoom/column/row). For OpenStreetMap
that's the standard template below; whatever source you pick, pass its required
credit via `attribution`:

```blade
<x-wirekit::map
    provider="leaflet"
    style-url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="© OpenStreetMap contributors"
    :center="[52.52, 13.405]"
    :zoom="12"
    :markers="$stores"
/>
```

OpenStreetMap's tile policy **requires** a visible `© OpenStreetMap contributors`
credit on every map. A linked credit is best practice:

```blade
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
```

::: warning
The `attribution` prop is **not optional** for most tile sources. Omitting the
OpenStreetMap credit violates the OSM tile usage policy and the ODbL data
license — your tile access can be **blocked without notice**. Commercial
providers have their own mandatory attribution. Never ship a tiled map
without it.
:::

## Store Locator

A multi-marker Leaflet map over OpenStreetMap tiles — every location is a marker
on the canvas **and** a focusable row in the accessible list.

<!-- markdownlint-disable MD034 -->

:::preview{title="Store locator on Leaflet + OpenStreetMap tiles"}
<x-wirekit::map
    provider="leaflet"
    style-url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="© OpenStreetMap contributors"
    :center="[51.1657, 10.4515]"
    :zoom="6"
    aria-label="Store locations across Germany"
    list-label="Stores"
    highlight="fill"
    highlight-color="success"
    :markers="[
        ['id' => 'b', 'lat' => 52.5200, 'lng' => 13.4050, 'label' => 'Kaufhaus Brandt — Berlin Mitte · Open', 'body' => 'Mo–Sa 9:00–20:00', 'intent' => 'success'],
        ['id' => 'm', 'lat' => 48.1351, 'lng' => 11.5820, 'label' => 'Brandt Stachus — Munich · Busy', 'body' => 'Mo–Sa 9:30–20:00', 'intent' => 'warning'],
        ['id' => 'h', 'lat' => 53.5511, 'lng' => 9.9937, 'label' => 'Brandt Hafencity — Hamburg · Open', 'body' => 'Mo–Sa 10:00–19:00', 'intent' => 'success'],
        ['id' => 'c', 'lat' => 50.9375, 'lng' => 6.9603, 'label' => 'Brandt Passage — Cologne · Closed today', 'body' => 'Tu–Sa 10:00–18:00', 'intent' => 'danger'],
    ]"
/>
:::

The `intent` on each marker colors both its list dot and — once a map engine is
loaded — the map pin itself; hovering a pin shows the store name with its opening
hours (the `body` line). The status word in each label carries the meaning, so the
locator never relies on color alone. This demo also opts into the `fill` selection
highlight (`highlight="fill" highlight-color="success"`) — click a store to see
the tinted-tile alternative to the default ring frame.

## Pin Tooltips

Pin tooltips work the same on Leaflet as on MapLibre — the bubble content follows
the marker's data shape, with no extra configuration:

- **Text only** — just a `label`: a single bold line.
- **Styled text** — `label` + `body`: a small card with the name on top and a
  muted detail line (address, opening hours) below.
- **Text with photo** — add an `image` URL: the photo renders as a card above
  the text, like showing the storefront at its location.
- **Photo only** — `image` + `tooltip: 'image'`: the bubble is just the photo;
  the label still names the pin for screen readers and stays in the marker list.

Sanitize image URLs you don't control — the adapter escapes the `src`.

:::preview{title="One Leaflet map, four tooltip variants"}
<x-wirekit::map
    provider="leaflet"
    style-url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="© OpenStreetMap contributors"
    :center="[52.5200, 13.4050]"
    :zoom="11"
    aria-label="Tooltip variants across Berlin"
    list-label="Stores"
    :markers="[
        ['id' => 'text', 'lat' => 52.5200, 'lng' => 13.4050, 'label' => 'Brandt Mitte — text only', 'intent' => 'accent'],
        ['id' => 'styled', 'lat' => 52.4990, 'lng' => 13.4030, 'label' => 'Brandt Kreuzberg — styled', 'body' => 'Mo–Sa 10:00–19:00 · Oranienstr. 12', 'intent' => 'success'],
        ['id' => 'photo', 'lat' => 52.5400, 'lng' => 13.4240, 'label' => 'Brandt Prenzlauer Berg — with photo', 'body' => 'Tu–Sa 11:00–19:00', 'image' => '/placeholder/320x180?bg=4f46e5&fg=ffffff&label=P-Berg&gradient=none', 'intent' => 'warning'],
        ['id' => 'imgonly', 'lat' => 52.5100, 'lng' => 13.3770, 'label' => 'Brandt Tiergarten — photo only', 'image' => '/placeholder/320x180?bg=0f766e&fg=ffffff&label=Tiergarten&gradient=none', 'tooltip' => 'image', 'intent' => 'neutral'],
    ]"
/>
:::

## Street-Level Locations

OSM tiles carry full street detail at high zoom, so several locations across a
city read clearly. Every marker is a list entry too — the accessible companion.

:::preview{title="Street-level locations on OpenStreetMap tiles"}
<x-wirekit::map
    provider="leaflet"
    style-url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="© OpenStreetMap contributors"
    :center="[52.515, 13.392]"
    :zoom="13"
    aria-label="Locations across Berlin"
    list-label="Locations"
    :markers="[
        ['id' => 'gate', 'lat' => 52.5163, 'lng' => 13.3777, 'label' => 'Brandenburg Gate — Pariser Platz', 'body' => 'Open 24 hours · free', 'intent' => 'success'],
        ['id' => 'cathedral', 'lat' => 52.5191, 'lng' => 13.4010, 'label' => 'Berlin Cathedral — Museum Island', 'body' => 'Mo–Sa 10:00–17:00 · dome closed today', 'intent' => 'warning'],
        ['id' => 'alex', 'lat' => 52.5219, 'lng' => 13.4132, 'label' => 'Alexanderplatz — TV Tower', 'body' => 'Daily 10:00–22:00', 'intent' => 'accent'],
        ['id' => 'checkpoint', 'lat' => 52.5076, 'lng' => 13.3904, 'label' => 'Checkpoint Charlie — Friedrichstraße', 'body' => 'Museum daily 10:00–20:00', 'intent' => 'accent'],
        ['id' => 'potsdamer', 'lat' => 52.5096, 'lng' => 13.3759, 'label' => 'Potsdamer Platz — Sony Center', 'body' => 'Plaza open 24 hours', 'intent' => 'neutral'],
    ]"
/>
:::

## Map Only

Drop the sidebar with `list="false"` for a clean Leaflet canvas — the list stays
`sr-only` for assistive tech.

:::preview{title="Map only — Leaflet, no sidebar"}
<x-wirekit::map
    provider="leaflet"
    style-url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="© OpenStreetMap contributors"
    list="false"
    :center="[52.5200, 13.4050]"
    :zoom="12"
    aria-label="Berlin"
    :markers="[
        ['id' => 'b', 'lat' => 52.5200, 'lng' => 13.4050, 'label' => 'Berlin — Mitte', 'intent' => 'accent'],
    ]"
/>
:::

<!-- markdownlint-enable MD034 -->

## OSM Usage Policy & Cost

The public OSM tile server (`tile.openstreetmap.org`) is run by volunteers on a
best-effort basis. Its [tile usage policy](https://operations.osmfoundation.org/policies/tiles/)
sets hard limits:

- **No bulk downloading** or pre-fetching tiles the user isn't actively viewing.
- **Cache tiles** and send a valid, identifying `User-Agent`.
- **No SLA** — availability is best-effort and access can be withdrawn at any time.
- **Not for heavy or commercial production** dependent on guaranteed uptime.

For anything beyond a prototype or low-traffic site, use a **commercial tile
provider** (which bills per load and handles attribution) or **self-host** OSM
tiles you render from the data yourself. Either way the `styleUrl` + `attribution`
props stay the same — only the tile host changes.

## MapLibre or Leaflet?

| | Leaflet | MapLibre GL |
| --- | --- | --- |
| Tiles | Raster (image) | Vector (styleable) |
| Footprint | ~40 KB | ~200 KB |
| Styling | Tile-baked | Full client-side styling |
| Best for | A small footprint + any raster source | Custom-styled, themeable maps |

Pick Leaflet when you want a small dependency and a ready raster tile source;
pick [MapLibre GL](/components/map/maplibre) when you want to style the map
yourself. The `<x-wirekit::map>` API and the accessible list are identical either
way — only `provider` and the tiles change.

## Licenses

Two separate licenses apply on this page, one per layer:

- **Leaflet (the engine)** is distributed under the **BSD-2-Clause** license (a
  permissive open-source license). It is an optional peer dependency — you
  install and load it; WireKit never bundles or relicenses it.
- **OpenStreetMap (the data + tiles)** is published under the **Open Database
  License (ODbL) 1.0**. The required credit for derived maps and tiles is
  **© OpenStreetMap contributors**.

## See Also

- [Map overview](/components/map) — the engine-independent API, markers, list, and Livewire wiring
- [MapLibre GL](/components/map/maplibre) — the vector-tile default engine
- [Leaflet documentation](https://leafletjs.com/)
- [OpenStreetMap copyright & license](https://www.openstreetmap.org/copyright)
- [OSM tile usage policy](https://operations.osmfoundation.org/policies/tiles/)
