Skip to content

Display Raster Tiles in Leaflet

Basic Map

This example adds Maptoolkit raster tiles as a tile layer to a Leaflet map:

let map = L.map("map").setView([47.310897, 12.805988], 12);
    L.tileLayer("https://rtc-cdn.maptoolkit.net/rtc/maptoolkit-maptoolkit.summer/{z}/{x}/{y}{ratio}.png?api_key=YOUR_API_KEY", {
      ratio: L.Browser.retina ? "@2x" : "",
      maxZoom: 18,
      attribution:
        "© <a href='https://www.maptoolkit.com' target='_blank'>Maptoolkit</a> \
        © <a href='https://www.openstreetmap.org/copyright' target='_blank'>OSM</a>",
    }).addTo(map);
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css" />
  <style>
    html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
    #map { width: 100%; height: 100%; }
  </style>
</head>
<body>
  <div id="map"></div>
  <script>
    let map = L.map("map").setView([47.310897, 12.805988], 12);
    L.tileLayer("https://rtc-cdn.maptoolkit.net/rtc/maptoolkit-maptoolkit.summer/{z}/{x}/{y}{ratio}.png?api_key=YOUR_API_KEY", {
      ratio: L.Browser.retina ? "@2x" : "",
      maxZoom: 18,
      attribution:
        "© <a href='https://www.maptoolkit.com' target='_blank'>Maptoolkit</a> \
        © <a href='https://www.openstreetmap.org/copyright' target='_blank'>OSM</a>",
    }).addTo(map);
  </script>
</body>
</html>

Switching Styles

Each Standard Map Style is its own raster endpoint, so you can offer several styles through a Leaflet layer control. Add one L.tileLayer per style and pass them to L.control.layers as base layers:

const attribution =
      "© <a href='https://www.maptoolkit.com' target='_blank'>Maptoolkit</a> \
       © <a href='https://www.openstreetmap.org/copyright' target='_blank'>OSM</a>";

    // Build a raster tile layer for one of the Standard Map Styles.
    // `name` is the maptoolkit-maptoolkit.<variant> id.
    function style(name) {
      return L.tileLayer(
        "https://rtc-cdn.maptoolkit.net/rtc/" + name + "/{z}/{x}/{y}{ratio}.png?api_key=YOUR_API_KEY",
        { ratio: L.Browser.retina ? "@2x" : "", maxZoom: 18, attribution: attribution }
      );
    }

    const baseLayers = {
      "Summer": style("maptoolkit-maptoolkit.summer"),
      "Winter": style("maptoolkit-maptoolkit.winter"),
      "Hiking": style("maptoolkit-maptoolkit.hiking"),
      "Cycling": style("maptoolkit-maptoolkit.cycling"),
      "Street": style("maptoolkit-maptoolkit.street"),
      "Light": style("maptoolkit-maptoolkit.light"),
      "Dark": style("maptoolkit-maptoolkit.dark"),
    };

    const map = L.map("map", {
      center: [47.310897, 12.805988],
      zoom: 12,
      layers: [baseLayers["Summer"]],
    });

    L.control.layers(baseLayers, null, { collapsed: false }).addTo(map);
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css" />
  <style>
    html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
    #map { width: 100%; height: 100%; }
  </style>
</head>
<body>
  <div id="map"></div>
  <script>
    const attribution =
      "© <a href='https://www.maptoolkit.com' target='_blank'>Maptoolkit</a> \
       © <a href='https://www.openstreetmap.org/copyright' target='_blank'>OSM</a>";

    // Build a raster tile layer for one of the Standard Map Styles.
    // `name` is the maptoolkit-maptoolkit.<variant> id.
    function style(name) {
      return L.tileLayer(
        "https://rtc-cdn.maptoolkit.net/rtc/" + name + "/{z}/{x}/{y}{ratio}.png?api_key=YOUR_API_KEY",
        { ratio: L.Browser.retina ? "@2x" : "", maxZoom: 18, attribution: attribution }
      );
    }

    const baseLayers = {
      "Summer": style("maptoolkit-maptoolkit.summer"),
      "Winter": style("maptoolkit-maptoolkit.winter"),
      "Hiking": style("maptoolkit-maptoolkit.hiking"),
      "Cycling": style("maptoolkit-maptoolkit.cycling"),
      "Street": style("maptoolkit-maptoolkit.street"),
      "Light": style("maptoolkit-maptoolkit.light"),
      "Dark": style("maptoolkit-maptoolkit.dark"),
    };

    const map = L.map("map", {
      center: [47.310897, 12.805988],
      zoom: 12,
      layers: [baseLayers["Summer"]],
    });

    L.control.layers(baseLayers, null, { collapsed: false }).addTo(map);
  </script>
</body>
</html>

How it works

Leaflet renders pre-rendered PNG images, so there is no style JSON and no WebGL. The tile URL carries everything: the style id, the {z}/{x}/{y} position and your api_key.

{ratio} is a Maptoolkit placeholder rather than a Leaflet one, which is why it is paired with the ratio option. L.Browser.retina ? "@2x" : "" fills it with @2x on high-density screens and an empty string elsewhere, so the same URL serves both. Drop the option and the literal text {ratio} ends up in the request.

Attribution is your job here. A vector style JSON carries an attribution string that the library displays on its own; a raster tile URL carries nothing. Leaving the attribution option out produces a map with no credit, which the license does not allow.

The style switcher builds one L.tileLayer per style and hands them to L.control.layers. The ids follow maptoolkit-maptoolkit.<variant>, and the seven variants are summer, winter, hiking, cycling, street, light and dark.

Leaflet takes [latitude, longitude], the opposite order from MapLibre and Mapbox GL JS.

Next steps

With the basemap working, the next move is your data on top. Leaflet handles that with L.geoJSON, L.marker and L.polyline, none of which care that the tiles underneath are raster.

The style switcher above is also the shape of a more useful control than it looks. Summer, Winter, Hiking and Cycling are genuinely different maps rather than palettes, so letting a user pick is worth doing on anything outdoor. If you later need to restyle rather than swap, that is the point to move to Vector Tiles, which restyle on the client.