A map block for Kirby with markers and paths, based on Leaflet and OpenStreetMap. No API key, no Google, and with an optional tile proxy your visitors never contact a third-party server.
- Map block for Kirby's block and layout editor
- Markers with title, description, size and color
- 30 marker symbols like stops, parking, cafés or museums, plus your own SVGs
- Optional legend below the map
- Links in descriptions for further information
- Paths with their own color, drawn directly on the map in the panel
- Location picker in the panel: search an address, click on the map or drag the marker
- The map automatically shows all locations and paths, or centers on a chosen location
- Labels always visible, on hover or only in the popup, per map or per location
- Embedded map or a thumbnail that opens the map in an overlay
- Default, minimum and maximum zoom per map
- Privacy friendly: tile proxy on your own server or a "load map" button before anything is loaded
- Several map styles (OpenStreetMap, OpenTopoMap, CyclOSM, …) to choose per map, optionally switchable by visitors
- Works with any Leaflet tile provider
- English and German translations, works on single- and multi-language sites
- Kirby 4 or 5
- PHP 8.1+
composer require tearoom1/kirby-leaflet-map
git submodule add https://github.com/tearoom1/kirby-leaflet-map.git site/plugins/leaflet-map
Download and copy this repository to /site/plugins/leaflet-map.
Add the block to the fieldsets of your blocks or layout field:
fieldsets:
- leaflet-mapInclude the plugin's CSS and JavaScript in your templates, e.g. in the header and footer snippets:
<?php snippet('leaflet-map/css') ?>
<?php snippet('leaflet-map/js') ?>Each map block can be configured in the panel:
- Title and description
- Display mode: embedded or a thumbnail that opens the map in an overlay
- Zoom levels: default, minimum and maximum
- Labels: set per location, always visible, on hover or only in the popup
- Legend: shown below the map
- Map style, if the site offers several (see Map styles)
- Locations: title, description, size (1–5), color, symbol, legend entry, label, hidden, map center
- Paths: name, color, label, legend entry and the line drawn on the map
Without a location marked as map center, the map shows all locations and paths. It zooms in at most to the default zoom and out below the minimum zoom if needed.
The title of a location appears as a label above the marker and in the popup when the marker is clicked, the description only in the popup. Descriptions support Kirbytext, so they can link to further information: [Opening hours](https://example.com) or (link: https://example.com text: Opening hours).
Each location can show a symbol on its marker: stop (H), bus, train, tram, bicycle, car, parking, charging station, harbour, restaurant, café, bar, accommodation, camping, shop, information, entrance, toilet, accessible, hospital, museum, church, photo spot, park, mountain, swimming, home, highlight, favourite and flag. The symbol is drawn in white, or dark on light marker colors.
The legend explains the markers and paths of the map. Locations and paths with the same legend entry share one line, e.g. "Our shops" or "Bike route". Without an entry, locations are listed by their symbol, while markers without a symbol share one line "Location" and paths one line "Path". As colors may differ within these groups, the legend shows neutral symbols. Only own legend entries whose locations or paths all have the same color show that color, e.g. "Sold out" in red.
Add your own symbols or remove built-in ones with the icons option. Symbols are SVGs with a 0 0 24 24 view box that use currentColor:
'tearoom1.leaflet-map.icons' => [
'ferry' => [
'label' => ['en' => 'Ferry', 'de' => 'Fähre'],
'svg' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">…</svg>',
],
'car' => false,
],By default all maps use the tile layer of the tiles options. With the layers option, editors can choose a style per map and allow visitors to switch between them:
'tearoom1.leaflet-map.layers' => [
'osm', // OpenStreetMap
'osm-de', // OpenStreetMap Deutschland
'topo', // OpenTopoMap
'cyclosm', // CyclOSM
'humanitarian',
'default', // the tiles.* options
'satellite' => [
'label' => 'Satellite',
'url' => 'https://tiles.example.com/{z}/{x}/{y}.jpg',
'attribution' => '…',
'maxZoom' => 18,
],
],The first style is the default. All presets are free OpenStreetMap based services; please respect their usage policies, or use the tile proxy, which works for all styles.
The presets cover the whole world. Official maps of a single country are often calmer and free as well, but stay empty outside of it, so they are not included as presets. Add them as own styles if your maps stay within that country, e.g. basemap.de for Germany or The National Map of the USGS for the United States. Note the {y}/{x} order in their URLs:
'tearoom1.leaflet-map.layers' => [
'osm',
// Germany only
'basemap-de' => [
'label' => 'basemap.de',
'url' => 'https://sgx.geodatenzentrum.de/wmts_basemapde/tile/1.0.0/de_basemapde_web_raster_farbe/default/GLOBAL_WEBMERCATOR/{z}/{y}/{x}.png',
'attribution' => '© <a href="https://basemap.de">basemap.de</a> / BKG | Datenquellen: © GeoBasis-DE',
'maxZoom' => 19,
],
// United States only
'usgs-topo' => [
'label' => 'USGS Topo',
'url' => 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
'attribution' => 'Tiles courtesy of the <a href="https://www.usgs.gov/">U.S. Geological Survey</a>',
'maxZoom' => 16,
],
],For basemap.de, de_basemapde_web_raster_grau gives a gray version, for the USGS, USGSImageryOnly instead of USGSTopo gives aerial images. Providers that need an API key, like Stadia Maps, Thunderforest or MapTiler, work the same way with the key in the URL; check their terms for your site.
Paths are drawn with the leaflet-path field: click on the map to add a point, drag a point to move it and click it to remove it. It stores a list of points:
foreach ($page->route()->yaml() as $point) {
echo $point['lat'] . ', ' . $point['lng'];
}Locations and path points are set with the leaflet-location field. Search an address, click on the map to place the marker, drag it to adjust it or enter the coordinates directly.
The field can be used in your own blueprints as well:
location:
label: Location
type: leaflet-location
center: [48.137, 11.576] # optional, map center while no location is set
zoom: 12 # optionalIt stores the coordinates as lat and lng:
$location = $page->location()->yaml();
echo $location['lat'] . ', ' . $location['lng'];The address search uses Nominatim by default. Requests are sent from your server, are only available to logged-in panel users, are limited to one per second and are cached for a week, as required by the Nominatim usage policy.
By default, map tiles are loaded from the OpenStreetMap servers, which transfers the IP address of your visitors to them. There are two ways to avoid this:
'tearoom1.leaflet-map.tiles.proxy' => true,Tiles are fetched by your server and stored in media/leaflet-map/tiles. From then on your web server delivers them directly, without PHP. Each map only allows the tiles around its own locations and within its zoom levels, so the proxy can't be used to download arbitrary tiles. The cache is renewed every month.
Make sure your server is allowed to connect to the tile provider and has some disk space for the tiles. For sites with a lot of traffic, use your own or a commercial tile provider (see tiles.url), the OpenStreetMap tile usage policy applies.
'tearoom1.leaflet-map.loadOnClick' => true,Embedded maps show a notice and a "Load map" button. Nothing is loaded from the tile server until the visitor clicks it.
All options are optional:
return [
'tearoom1.leaflet-map' => [
'tiles.url' => 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
'tiles.attribution' => '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
'tiles.maxZoom' => 19,
'tiles.proxy' => false,
'tiles.userAgent' => 'kirby-leaflet-map (+https://example.com)',
'loadOnClick' => false,
'geocoder' => true,
'geocoder.url' => 'https://nominatim.openstreetmap.org/search',
'panel.center' => [20, 0],
'panel.zoom' => 2,
'layers' => ['default'],
'icons' => [],
'alwaysIncludeAssets' => true,
'enabled' => true,
],
];| Option | Default | Description |
|---|---|---|
tiles.url |
OpenStreetMap | Tile URL template for Leaflet, any provider works |
tiles.attribution |
OpenStreetMap | Attribution shown on the map, required by most providers |
tiles.maxZoom |
19 |
Highest zoom level the tile provider offers |
tiles.proxy |
false |
Load tiles through your own server (see Privacy) |
tiles.userAgent |
plugin name and site URL | User agent for requests to the tile provider and geocoder |
loadOnClick |
false |
Embedded maps only load after a click (see Privacy) |
geocoder |
true |
Address search in the location picker |
geocoder.url |
Nominatim | Nominatim compatible search endpoint |
layers |
['default'] |
Map styles editors can choose from (see Map styles) |
icons |
[] |
Own marker symbols, false removes a built-in one (see Symbols and legend) |
panel.center |
[20, 0] |
Center of the picker map while no location is set |
panel.zoom |
2 |
Zoom of the picker map while no location is set |
alwaysIncludeAssets |
true |
Include CSS and JS on every page, otherwise only on pages with a map block |
enabled |
true |
Set to false to stop including the plugin's CSS and JS |
Texts can be changed with Kirby's translations, the keys start with tearoom1.leaflet-map.
This plugin is licensed under the MIT License
- Mathis Koblin
- Built with Leaflet
- Marker symbols from Lucide (ISC License)
- Map data © OpenStreetMap contributors, address search by Nominatim
Both previews use the same Munich map at 1408 × 902 pixels. The dark preview uses site CSS to theme the OpenStreetMap tiles, labels and map controls.

