Skip to content

Use Maptoolkit Tiles with Vue Leaflet

vue-leaflet wraps Leaflet in Vue 3 components. Maptoolkit raster tiles go into its LTileLayer, and the map’s center and zoom can be bound to your own state with v-model.

Install

npm install leaflet @vue-leaflet/vue-leaflet

The component

<script setup>
import { ref } from "vue";
import { LMap, LTileLayer, LMarker, LPopup } from "@vue-leaflet/vue-leaflet";
import "leaflet/dist/leaflet.css";

const KEY = import.meta.env.VITE_MAPTOOLKIT_KEY;
const zoom = ref(13);
const center = ref([47.2692, 11.4041]);
</script>

<template>
  <div style="height: 400px">
    <l-map v-model:zoom="zoom" v-model:center="center">
      <l-tile-layer
        :url="`https://rtc-cdn.maptoolkit.net/rtc/maptoolkit-maptoolkit.summer/{z}/{x}/{y}{r}.png?api_key=${KEY}`"
        :max-zoom="18"
        attribution='&copy; <a href="https://www.maptoolkit.com">Maptoolkit</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
      />
      <l-marker :lat-lng="[47.2692, 11.4041]">
        <l-popup>Innsbruck</l-popup>
      </l-marker>
    </l-map>
  </div>
</template>
  • LMap fills its parent, so the wrapping element needs a height or the map is zero pixels tall.
  • The stylesheet import is required. Without leaflet/dist/leaflet.css, the tiles render as a scattered mosaic across the page.
  • {r} is Leaflet’s retina placeholder. It becomes @2x on high-density screens and an empty string elsewhere, and Maptoolkit serves both tile sizes from the same URL.
  • The attribution prop is required. Raster tiles carry no credit of their own.

The default marker icon works in a Vite build as it is, in the development server and after npm run build. The icon fix React Leaflet needs is not necessary here.

Moving the map

v-model:center and v-model:zoom bind both ways. Panning the map updates center and zoom, and assigning a new value to center moves the map. After the user pans, center holds a { lat, lng } object, so read it as center.value.lat.

Changing center and zoom in the same step only applies the zoom. Setting both refs one after the other in one function leaves the map at the old center with the new zoom. For a move that changes both, call Leaflet directly: the ready event hands you the Leaflet map.

<script setup>
import { ref, shallowRef } from "vue";

const zoom = ref(13);
const center = ref([47.2692, 11.4041]);
const leafletMap = shallowRef(null);

function flyToSalzburg() {
  leafletMap.value?.flyTo([47.8095, 13.055], 12);
}
</script>

<template>
  <button @click="flyToSalzburg">Salzburg</button>
  <div style="height: 400px">
    <l-map v-model:zoom="zoom" v-model:center="center" @ready="(map) => (leafletMap = map)">
      <!-- tile layer as above -->
    </l-map>
  </div>
</template>

flyTo() animates both at once, and v-model picks up the result, so center and zoom are in sync again when the animation ends. Keep the Leaflet map in a shallowRef: a plain ref makes Vue proxy the whole map object, for the reasons described on the Maps JS Vue page.

Where to put the API key

The example reads the key from VITE_MAPTOOLKIT_KEY, the Vite convention, and Vite writes it into the JavaScript the browser downloads. A map key is always visible in the browser, so restrict it to your domains in your account.

Next steps

Your own data goes on top with LGeoJson, LPolyline and LPolygon. The Leaflet GeoJSON example shows a per-feature style function and popups. LGeoJson takes the data as geojson, the style function as options-style, and onEachFeature inside options.

For a style per season, each Maptoolkit style is one more LTileLayer inside LControlLayers, the same pattern the raster tiles Leaflet example builds with plain Leaflet.