Vue 3 Quickstart
Install
bash
npm install maplibre-glComponent
vue
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
import maplibregl, { type Map } from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
const el = ref<HTMLDivElement>();
let map: Map | null = null;
const KEY = import.meta.env.VITE_MFD_PUB_KEY;
onMounted(() => {
if (!el.value) return;
map = new maplibregl.Map({
container: el.value,
style: `https://api.mapsfordevs.com/styles/standard.json?key=${KEY}`,
center: [28.04, -26.20],
zoom: 11
});
});
onBeforeUnmount(() => {
map?.remove();
map = null;
});
</script>
<template>
<div ref="el" class="w-full h-screen" />
</template>Composable
ts
// composables/useMapLibre.ts
import { onMounted, onBeforeUnmount, ref, type Ref } from 'vue';
import maplibregl, { type MapOptions, type Map } from 'maplibre-gl';
export function useMapLibre(container: Ref<HTMLElement | undefined>, opts: Omit<MapOptions, 'container'>) {
const map = ref<Map | null>(null);
onMounted(() => {
if (!container.value) return;
map.value = new maplibregl.Map({ container: container.value, ...opts });
});
onBeforeUnmount(() => {
map.value?.remove();
map.value = null;
});
return { map };
}Usage:
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useMapLibre } from '@/composables/useMapLibre';
const el = ref<HTMLDivElement>();
const { map } = useMapLibre(el, {
style: `https://api.mapsfordevs.com/styles/standard.json?key=${import.meta.env.VITE_MFD_PUB_KEY}`,
center: [28.04, -26.20],
zoom: 11
});
</script>
<template><div ref="el" class="w-full h-screen" /></template>Language switcher (vue-i18n integration)
vue
<script setup lang="ts">
import { watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useMapLibreContext } from '@/composables/useMapLibre';
const { locale } = useI18n();
const { map } = useMapLibreContext();
watch([locale, map], ([newLocale, newMap]) => {
if (!newMap) return;
const apply = () => {
['place_label', 'transportation_name', 'poi_label'].forEach(layer => {
newMap.setLayoutProperty(layer, 'text-field', [
'coalesce',
['get', `name:${newLocale}`],
['get', 'name:latin'],
['get', 'name']
]);
});
};
newMap.isStyleLoaded() ? apply() : newMap.once('styledata', apply);
});
</script>Pinia store for shared map state
ts
// stores/map.ts
import { defineStore } from 'pinia';
import { ref } from 'vue';
import type { Map } from 'maplibre-gl';
export const useMapStore = defineStore('map', () => {
const instance = ref<Map | null>(null);
const setMap = (m: Map) => { instance.value = m; };
return { instance, setMap };
});Geocoding composable
ts
// composables/useGeocode.ts
import { ref } from 'vue';
export function useGeocode(apiKey: string) {
const results = ref<any[]>([]);
const loading = ref(false);
async function search(q: string) {
loading.value = true;
try {
const r = await fetch(
`https://api.mapsfordevs.com/geocode/forward?q=${encodeURIComponent(q)}&limit=5`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
const body = await r.json();
if (body.ok) results.value = body.items;
} finally {
loading.value = false;
}
}
return { results, loading, search };
}