Configuration
Vue3 MapLibre GL provides extensive configuration options for maps, components, and composables. This guide covers the most common configuration scenarios.
Map Configuration
Basic Map Options
The Maplibre component accepts a comprehensive options prop that mirrors MapLibre GL's MapOptions:
vue
<template>
<Maplibre :options="mapOptions" />
</template>
<script setup>
import { ref } from 'vue';
import { Maplibre } from 'vue3-maplibre-gl';
const mapOptions = ref({
// Style
style: 'https://demotiles.maplibre.org/style.json',
// Initial view
center: [0, 0],
zoom: 2,
bearing: 0,
pitch: 0,
// Zoom limits
minZoom: 0,
maxZoom: 22,
// Pitch limits
minPitch: 0,
maxPitch: 60,
// Bounds
maxBounds: [
[-180, -85],
[180, 85],
],
// Interaction
interactive: true,
scrollZoom: true,
boxZoom: true,
dragRotate: true,
dragPan: true,
keyboard: true,
doubleClickZoom: true,
touchZoomRotate: true,
// Controls
attributionControl: true,
// Performance
antialias: true,
optimizeForTerrain: true,
// Localization
locale: {
'AttributionControl.ToggleAttribution': 'Toggle attribution',
'AttributionControl.MapFeedback': 'Map feedback',
},
});
</script>Advanced Configuration
vue
<script setup>
import { ref } from 'vue';
const advancedMapOptions = ref({
style: {
version: 8,
sources: {},
layers: [],
glyphs: 'https://fonts.openmaptiles.org/{fontstack}/{range}.pbf',
sprite: 'https://sprites.example.com/sprite',
},
// Custom projection (MapLibre GL v3+)
projection: 'mercator',
// Terrain
terrain: {
source: 'terrain-source',
exaggeration: 1.5,
},
// Fog
fog: {
color: 'rgb(186, 210, 235)',
'high-color': 'rgb(36, 92, 223)',
'horizon-blend': 0.02,
'space-color': 'rgb(11, 11, 25)',
'star-intensity': 0.6,
},
// Light
light: {
anchor: 'viewport',
color: 'white',
intensity: 0.4,
},
// Performance optimizations
fadeDuration: 300,
crossSourceCollisions: true,
collectResourceTiming: false,
// Custom transform request
transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith('http://myHost')) {
return {
url: url.replace('http', 'https'),
headers: { 'my-custom-header': true },
credentials: 'include',
};
}
},
});
</script>Component Configuration
GeoJsonSource Configuration
vue
<template>
<Maplibre :options="mapOptions">
<GeoJsonSource
id="my-source"
:data="geoJsonData"
:options="sourceOptions"
:debug="true"
@load="onSourceLoad"
@error="onSourceError"
>
<FillLayer :style="fillStyle" />
</GeoJsonSource>
</Maplibre>
</template>
<script setup>
import { ref } from 'vue';
const sourceOptions = ref({
// Clustering
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50,
clusterMinPoints: 2,
clusterProperties: {
sum: ['+', ['get', 'value']],
max: ['max', ['get', 'value']],
},
// Performance
buffer: 128,
tolerance: 0.375,
maxzoom: 18,
// Line metrics for LineString features
lineMetrics: true,
// Generate feature IDs
generateId: true,
// Attribution
attribution: 'Data source attribution',
});
function onSourceLoad(source) {
console.log('Source loaded:', source);
}
function onSourceError(error) {
console.error('Source error:', error);
}
</script>Layer Configuration
vue
<template>
<GeoJsonSource :data="data">
<!-- Fill Layer -->
<FillLayer
id="polygons"
:style="fillStyle"
:filter="fillFilter"
:minzoom="5"
:maxzoom="15"
:metadata="{ description: 'Polygon layer' }"
/>
<!-- Circle Layer -->
<CircleLayer
id="points"
:style="circleStyle"
:filter="pointFilter"
source-layer="points"
/>
<!-- Line Layer -->
<LineLayer id="lines" :style="lineStyle" before-id="polygons" />
<!-- Symbol Layer -->
<SymbolLayer id="labels" :style="symbolStyle" :visible="showLabels" />
</GeoJsonSource>
</template>
<script setup>
import { ref } from 'vue';
// Layer styles with expressions
const fillStyle = ref({
'fill-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#627BC1',
['interpolate', ['linear'], ['get', 'value'], 0, '#F2F12D', 100, '#E55E5E'],
],
'fill-opacity': ['interpolate', ['linear'], ['zoom'], 5, 0.5, 15, 0.8],
});
const circleStyle = ref({
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
5,
['*', ['get', 'magnitude'], 2],
15,
['*', ['get', 'magnitude'], 10],
],
'circle-color': [
'interpolate',
['linear'],
['get', 'magnitude'],
1,
'#ffffcc',
5,
'#fd8d3c',
10,
'#800026',
],
'circle-stroke-width': 1,
'circle-stroke-color': '#fff',
});
const lineStyle = ref({
'line-color': '#007cbf',
'line-width': ['interpolate', ['linear'], ['zoom'], 5, 1, 15, 8],
'line-opacity': 0.8,
'line-dasharray': [2, 2],
});
const symbolStyle = ref({
'text-field': ['get', 'name'],
'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
'text-size': ['interpolate', ['linear'], ['zoom'], 10, 12, 16, 18],
'text-color': '#333',
'text-halo-color': '#fff',
'text-halo-width': 1,
'text-anchor': 'top',
'text-offset': [0, 1],
'icon-image': 'marker-icon',
'icon-size': 0.8,
});
// Filters
const fillFilter = ref(['==', ['geometry-type'], 'Polygon']);
const pointFilter = ref(['==', ['geometry-type'], 'Point']);
const showLabels = ref(true);
</script>Marker Configuration
vue
<template>
<Maplibre :options="mapOptions">
<Marker
:lnglat="markerPosition"
:options="markerOptions"
:draggable="true"
:popup="markerPopup"
@dragend="onMarkerDragEnd"
>
<div class="custom-marker">
<img src="/marker-icon.png" alt="Marker" />
</div>
</Marker>
</Maplibre>
</template>
<script setup>
import { ref } from 'vue';
import { MaplibrePopup } from 'vue3-maplibre-gl';
const markerPosition = ref([0, 0]);
const markerOptions = ref({
color: '#FF0000',
anchor: 'bottom',
offset: [0, -10],
clickTolerance: 5,
rotation: 0,
rotationAlignment: 'map',
pitchAlignment: 'auto',
scale: 1.2,
occludedOpacity: 0.2,
});
const markerPopup = ref(
new MaplibrePopup({
closeButton: true,
closeOnClick: false,
anchor: 'bottom',
offset: [0, -40],
}).setHTML('<h3>Custom Marker</h3><p>This is a custom marker with popup</p>'),
);
function onMarkerDragEnd(event) {
const lngLat = event.target.getLngLat();
markerPosition.value = [lngLat.lng, lngLat.lat];
console.log('Marker moved to:', markerPosition.value);
}
</script>
<style>
.custom-marker {
cursor: pointer;
transition: transform 0.2s;
}
.custom-marker:hover {
transform: scale(1.1);
}
.custom-marker img {
width: 32px;
height: 32px;
}
</style>Composable Configuration
Map Composables
vue
<script setup>
import { ref } from 'vue';
import {
useCreateMaplibre,
useFlyTo,
useMapEventListener,
} from 'vue3-maplibre-gl';
const mapContainer = ref();
const mapStyle = ref('https://demotiles.maplibre.org/style.json');
// Enhanced map creation with configuration
const {
mapInstance,
setCenter,
setZoom,
isMapReady,
isMapLoading,
hasMapError,
} = useCreateMaplibre(mapContainer, mapStyle, {
register: (actions) => {
console.log('Map actions registered:', actions);
},
debug: true,
onLoad: (map) => {
console.log('Map loaded successfully');
// Configure map after load
map.addControl(new NavigationControl(), 'top-right');
},
onError: (error) => {
console.error('Map loading error:', error);
},
});
// Animation configuration
const { flyTo, isFlying } = useFlyTo({
map: mapInstance,
});
// Event listener configuration
useMapEventListener({
map: mapInstance,
event: 'click',
handler: (event) => {
flyTo({
center: event.lngLat,
zoom: 12,
duration: 2000,
essential: true,
easing: (t) => t * (2 - t), // easeOutQuad
});
},
});
</script>Source and Layer Composables
vue
<script setup>
import { ref } from 'vue';
import {
useCreateGeoJsonSource,
useCreateFillLayer,
useLayerEventListener,
} from 'vue3-maplibre-gl';
const mapInstance = ref();
// Source configuration
const { sourceId, setData, isSourceReady } = useCreateGeoJsonSource({
map: mapInstance,
id: 'my-source',
data: {
type: 'FeatureCollection',
features: [],
},
options: {
cluster: true,
clusterMaxZoom: 14,
},
debug: true,
register: (actions, map) => {
console.log('Source registered:', actions);
},
});
// Layer configuration
const { getLayer, setStyle, setFilter } = useCreateFillLayer({
map: mapInstance,
source: sourceId,
id: 'fill-layer',
style: {
'fill-color': '#088',
'fill-opacity': 0.8,
},
register: (actions, map) => {
console.log('Layer registered:', actions);
},
});
// Layer event configuration
useLayerEventListener({
map: mapInstance,
layerId: 'fill-layer',
event: 'click',
handler: (event) => {
console.log('Layer clicked:', event.features[0]);
// Update layer style on click
setStyle({
'fill-color': '#ff0000',
'fill-opacity': 0.6,
});
},
});
</script>Environment Configuration
Development vs Production
js
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
define: {
__VUE_MAPLIBRE_DEBUG__: JSON.stringify(
process.env.NODE_ENV === 'development',
),
},
optimizeDeps: {
include: ['maplibre-gl', 'vue3-maplibre-gl'],
},
build: {
rollupOptions: {
external: process.env.NODE_ENV === 'production' ? [] : ['maplibre-gl'],
},
},
});Global Configuration
js
// main.js
import { createApp } from 'vue';
import VueMapLibreGl from 'vue3-maplibre-gl';
import 'vue3-maplibre-gl/dist/style.css';
const app = createApp(App);
// Global configuration
app.use(VueMapLibreGl, {
// Global debug mode
debug: process.env.NODE_ENV === 'development',
// Default map options
defaultMapOptions: {
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 0],
zoom: 2,
},
// Global error handler
onError: (error, context) => {
console.error('Vue MapLibre GL Error:', error, context);
// Send to error reporting service
},
});
app.mount('#app');This configuration guide covers the most common scenarios for customizing Vue3 MapLibre GL components and composables. For more advanced configurations, refer to the API documentation and MapLibre GL JS documentation.