Skip to content

Leaflet

Leaflet is a lightweight library for interactive maps. The community service is vector only, so you render the Maptoolkit vector style in Leaflet through the @maplibre/maplibre-gl-leaflet plugin, which draws a MapLibre GL layer inside a Leaflet map. No API key is required.

Leaflet does not support vector tiles natively, leading to noticeable performance drops. We recommend using MapLibre for smooth, GPU-powered rendering.

Basic Setup

npm

npm install leaflet maplibre-gl @maplibre/maplibre-gl-leaflet @maptoolkit/leaflet-logo
import L from 'leaflet';
  import 'leaflet/dist/leaflet.css';
  import 'maplibre-gl/dist/maplibre-gl.css';
  import '@maplibre/maplibre-gl-leaflet';
  import {MaptoolkitLogoControl} from '@maptoolkit/leaflet-logo';
  const map = L.map('map').setView([48.21, 16.37], 12);
  L.maplibreGL({
    style: 'https://styles.maptoolkit.org/summer.json',
    attribution:
      "© <a href='https://www.maptoolkit.com/copyright/'>Maptoolkit</a> " +
      "© <a href='https://www.openstreetmap.org/copyright'>OSM</a>",
  }).addTo(map);

  // Arabic/Hebrew: MapLibre has no right-to-left support built in, this plugin adds it
  maplibregl.setRTLTextPlugin('https://cdn.jsdelivr.net/npm/@mapbox/[email protected]/dist/mapbox-gl-rtl-text.js', false);
  new MaptoolkitLogoControl().addTo(map);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      margin: 0;
    }
    #map {
      height: 100vh;
    }
  </style>
  <title>Leaflet</title>
</head>

<body>
<div id="map"></div>
<script type="module">
  import L from 'leaflet';
  import 'leaflet/dist/leaflet.css';
  import 'maplibre-gl/dist/maplibre-gl.css';
  import '@maplibre/maplibre-gl-leaflet';
  import {MaptoolkitLogoControl} from '@maptoolkit/leaflet-logo';
  const map = L.map('map').setView([48.21, 16.37], 12);
  L.maplibreGL({
    style: 'https://styles.maptoolkit.org/summer.json',
    attribution:
      "© <a href='https://www.maptoolkit.com/copyright/'>Maptoolkit</a> " +
      "© <a href='https://www.openstreetmap.org/copyright'>OSM</a>",
  }).addTo(map);

  // Arabic/Hebrew: MapLibre has no right-to-left support built in, this plugin adds it
  maplibregl.setRTLTextPlugin('https://cdn.jsdelivr.net/npm/@mapbox/[email protected]/dist/mapbox-gl-rtl-text.js', false);
  new MaptoolkitLogoControl().addTo(map);
</script>
</body>

</html>

CDN

import * as maplibregl from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/maplibre-gl.mjs';

  // maplibre-gl-leaflet is UMD with no ESM build and reads the global when it
  // loads, so the assignment has to happen first - hence the dynamic import
  // instead of a <script> tag, whose order we could not control here.
  window.maplibregl = maplibregl;
  await import('https://cdn.jsdelivr.net/npm/@maplibre/[email protected]/leaflet-maplibre-gl.js');

  const map = L.map('map').setView([48.21, 16.37], 12);
  L.maplibreGL({
    style: 'https://styles.maptoolkit.org/summer.json',
    attribution:
      "© <a href='https://www.maptoolkit.com/copyright/'>Maptoolkit</a> " +
      "© <a href='https://www.openstreetmap.org/copyright'>OSM</a>",
  }).addTo(map);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet.css">
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/maplibre-gl.css">
  <style>
    body {
      margin: 0;
    }
    #map {
      height: 100vh;
    }
    #maptoolkit-attribution {
      position: absolute;
      height: 24px;
      bottom: 8px;
      left: 8px;
      z-index: 1000;
    }
  </style>
  <title>Leaflet</title>
</head>

<body>
<div id="map"></div>
<img id="maptoolkit-attribution" src="https://www.maptoolkit.org/assets/maptoolkit-attribution.png" alt="Maptoolkit">
<script type="module">
  import * as maplibregl from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/maplibre-gl.mjs';

  // maplibre-gl-leaflet is UMD with no ESM build and reads the global when it
  // loads, so the assignment has to happen first - hence the dynamic import
  // instead of a <script> tag, whose order we could not control here.
  window.maplibregl = maplibregl;
  await import('https://cdn.jsdelivr.net/npm/@maplibre/[email protected]/leaflet-maplibre-gl.js');

  const map = L.map('map').setView([48.21, 16.37], 12);
  L.maplibreGL({
    style: 'https://styles.maptoolkit.org/summer.json',
    attribution:
      "© <a href='https://www.maptoolkit.com/copyright/'>Maptoolkit</a> " +
      "© <a href='https://www.openstreetmap.org/copyright'>OSM</a>",
  }).addTo(map);
</script>
</body>

</html>

Examples

See the Examples gallery for markers, popups, and more on the vector basemap.

Attribution

Keep the Maptoolkit logo with @maptoolkit/leaflet-logo and the © Maptoolkit © Openstreetmap copyright line visible. See Quick Start - Add attribution for the full requirements.

Notes

This approach renders vector tiles through MapLibre GL, so it requires WebGL. For a vector-native map with smooth zooming and rotation, you can also use MapLibre GL JS directly.