Skip to content
MapLibre GL JS

Use Maptoolkit with Vue and MapLibre GL JS

vue-maplibre-gl wraps MapLibre GL JS in Vue 3 components. A Maptoolkit style URL goes into the map-style prop; the style names its own tile sources and carries its own attribution.

Install

npm install @indoorequal/vue-maplibre-gl maplibre-gl

The current version requires MapLibre GL JS 6 and Vue 3.4 or later.

The component

<script setup>
import "./maplibre-worker";
import { ref } from "vue";
import { MglMap, MglGeoJsonSource, MglLineLayer } from "@indoorequal/vue-maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";

const KEY = import.meta.env.VITE_MAPTOOLKIT_KEY;
const style = `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${KEY}`;

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

const route = {
  type: "Feature",
  geometry: { type: "LineString", coordinates: [[11.3928, 47.2654], [11.4041, 47.2692], [11.4205, 47.2731]] },
};
</script>

<template>
  <div style="height: 400px">
    <mgl-map :map-style="style" v-model:center="center" v-model:zoom="zoom" :attribution-control="{ compact: false }">
      <mgl-geo-json-source source-id="route" :data="route">
        <mgl-line-layer layer-id="route" :paint="{ 'line-color': '#d6336c', 'line-width': 5 }" />
      </mgl-geo-json-source>
    </mgl-map>
  </div>
</template>
  • Sources and layers are components. MglGeoJsonSource with an MglLineLayer inside adds both once the style has loaded, so there is no load event to wait for yourself.
  • v-model:center and v-model:zoom bind both ways. Panning updates the refs, and assigning new values moves the map, both at once if you change both. After the user pans, center holds a { lng, lat } object.
  • attribution-control with compact: false keeps the credit visible. By default MapLibre collapses it into an icon as soon as the user interacts with the map.
  • The wrapping element needs a height, or the map renders nothing and reports no error.

MapLibre 6 needs its worker set up

Without the first import, the map stays blank in a Vite app and the console shows Worker failed to load. MapLibre 6 loads its web worker from a separate file next to its own script, and the bundler moves the library away from it. Point MapLibre at the bundled worker once, before the first map is created:

// maplibre-worker.js
import { setWorkerUrl } from "maplibre-gl";
import workerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url";

setWorkerUrl(workerUrl);

It is the same fix as for react-map-gl, because the cause is MapLibre, not the wrapper. It covers both the development server and the production build.

Calling the map directly

useMap() returns the registered map instance, with the MapLibre map on .map and an isLoaded flag. With one map on the page it needs no argument; for several, give each MglMap a map-key and pass the same key to useMap():

import { useMap } from "@indoorequal/vue-maplibre-gl";

const mapInstance = useMap();

function flyToSalzburg() {
  mapInstance.map?.flyTo({ center: [13.055, 47.8095], zoom: 12 });
}

The v-model refs follow the animation, so they hold the new center and zoom once it ends.

Next steps

Every layer type has a component, from MglFillLayer to MglSymbolLayer, and their paint and layout props take the same objects as MapLibre’s addLayer(). That means the per-API MapLibre examples carry over directly: a route from the Routing API, or relief from Terrain Tiles.

For Maptoolkit Maps JS in Vue, see Using Maptoolkit maps in Vue.