Components

VMap

The core map component for rendering MapLibre GL maps

Usage

<script setup lang="ts">
  import { VMap } from '@geoql/v-maplibre';
  import type { MapOptions } from 'maplibre-gl';

  const mapOptions: MapOptions = {
    style: 'https://demotiles.maplibre.org/style.json',
    center: [-74.5, 40],
    zoom: 9,
  };

  const handleMapLoad = (map) => {
    console.log('Map loaded!', map);
  };
</script>

<template>
  <VMap
    :options="mapOptions"
    @loaded="handleMapLoad"
    style="height: 500px"
  ></VMap>
</template>

Props

options

  • Type: MapOptions
  • Required: true
  • Default: { container: 'map' }

MapLibre GL map options. See MapLibre GL documentation for all available options.

Common options include:

  • style: URL to the map style or a style object
  • center: Initial map center [lng, lat]
  • zoom: Initial zoom level
  • bearing: Initial bearing (rotation)
  • pitch: Initial pitch (tilt)
  • minZoom: Minimum zoom level
  • maxZoom: Maximum zoom level
  • maxBounds: Map bounds constraint

supportPmtiles

  • Type: boolean
  • Required: false
  • Default: false

Enable PMTiles protocol support. Set to true if you plan to use PMTiles layers.

<VMap :options="mapOptions" support-pmtiles></VMap>

globalState

  • Type: Record<string, unknown>
  • Required: false
  • Default: undefined

Reactive values for global-state expressions in the style. The object is the initial state (it overrides the defaults in the style's state property and takes precedence over options.globalState). After the map is created, each changed key calls map.setGlobalStateProperty, and a removed key resets to the style default. Nested changes (an array item, an object field) are detected.

<script setup lang="ts">
  import { ref } from 'vue';

  // The style filters a layer with ["global-state", "minPopulation"].
  const state = ref({ minPopulation: 100000 });
</script>

<template>
  <VMap :options="mapOptions" :global-state="state"></VMap>
  <input v-model.number="state.minPopulation" type="range" max="5000000" />
</template>

dragRotate

  • Type: boolean | DragRotateOptions
  • Required: false
  • Default: undefined (MapLibre default)

Reactive drag-to-rotate behaviour. false disables it and true uses the defaults. An object picks the pivot (MapLibre 6.15+):

  • { around: 'pointer' } turns and tilts around the point under the pointer, on the terrain where there is any.
  • { around: 'center', movement: 'circular' | 'linear' } turns around the map center.

Takes precedence over options.dragRotate.

<VMap :options="mapOptions" :drag-rotate="{ around: 'pointer' }"></VMap>

Events

@loaded

Emitted when the map has finished loading.

  • Payload: Map - The MapLibre GL Map instance
<VMap @loaded="(map) => console.log('Map ready', map)"></VMap>

Map Events

The VMap component forwards all MapLibre GL map events. You can listen to any map event using the @ directive:

<VMap
  :options="mapOptions"
  @click="handleClick"
  @move="handleMove"
  @zoom="handleZoom"
  @load="handleLoad"
></VMap>

Common events include:

  • @load - Map loaded
  • @click - Map clicked
  • @dblclick - Map double-clicked
  • @move - Map moved
  • @movestart - Map move started
  • @moveend - Map move ended
  • @zoom - Map zoomed
  • @zoomstart - Zoom started
  • @zoomend - Zoom ended

See MapLibre GL Events for the complete list.

Slots

default

The default slot is used to nest child components like markers, popups, layers, and controls.

<VMap :options="mapOptions">
  <VMarker :coordinates="[0, 0]" />
  <VLayerMaplibreGeojson :source="source" :layer="layer" />
  <VControlNavigation />
</VMap>

Accessing the Map Instance

Child components can access the map instance using Vue's provide/inject API:

<script setup lang="ts">
  import { inject } from 'vue';
  import { MapKey } from '@geoql/v-maplibre/utils/symbols';
  import type { Map } from 'maplibre-gl';
  import type { Ref } from 'vue';

  const map = inject<Ref<Map | null>>(MapKey);

  // Use the map instance
  watch(map, (mapInstance) => {
    if (mapInstance) {
      console.log('Map instance available', mapInstance);
    }
  });
</script>

TypeScript

The component is fully typed with TypeScript. Import types from maplibre-gl:

import type { MapOptions, Map } from 'maplibre-gl';

Examples

Custom Style

<script setup lang="ts">
  import { VMap } from '@geoql/v-maplibre';

  const customStyle = {
    version: 8,
    sources: {
      osm: {
        type: 'raster',
        tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
        tileSize: 256,
        attribution: '© OpenStreetMap Contributors',
      },
    },
    layers: [
      {
        id: 'osm',
        type: 'raster',
        source: 'osm',
      },
    ],
  };

  const mapOptions = {
    style: customStyle,
    center: [0, 0],
    zoom: 2,
  };
</script>

<template>
  <VMap :options="mapOptions" style="height: 100vh"></VMap>
</template>

With Bounds

<script setup lang="ts">
  import { VMap } from '@geoql/v-maplibre';
  import type { LngLatBoundsLike } from 'maplibre-gl';

  const bounds: LngLatBoundsLike = [
    [-74.047285, 40.679548], // Southwest
    [-73.907005, 40.882214], // Northeast
  ];

  const mapOptions = {
    style: 'https://demotiles.maplibre.org/style.json',
    bounds,
    fitBoundsOptions: {
      padding: 20,
    },
  };
</script>

<template>
  <VMap :options="mapOptions" style="height: 500px"></VMap>
</template>
Copyright © 2026