---
title: "MaplibreMap"
description: "The root component that creates a MapLibre GL instance on the client and distributes it via MaplibreContext, with v-model camera two-way binding and cross-route persistence."
canonical_url: "https://maplibre.mhaibaraai.cn/en/docs/core/map"
---
# MaplibreMap

> The root component that creates a MapLibre GL instance on the client and distributes it via MaplibreContext, with v-model camera two-way binding and cross-route persistence.

## Introduction

`MaplibreMap` is the root of everything: it creates the `maplibre-gl` instance on the client in `onMounted`, distributes a [MaplibreContext](https://maplibre.mhaibaraai.cn/docs/composables/use-map#api) via `provide`, and child components access it through `useMap()`. The container is sized at `100%` width and height — make sure the parent has an explicit height (examples use `h-115` throughout).

> [!NOTE]
> 
> The component is SSR-safe and does not require a 
> 
> <ClientOnly>
> 
>  wrapper. When 
> 
> options.style
> 
>  is omitted, a blank style is used (pair it with 
> 
> MaplibreTiandituLayer
> 
>  to overlay raster basemaps only).

## Usage

`center` / `zoom` / `bearing` / `pitch` all support `v-model`: the component compares the bound value against the current map state and only pushes an update when they differ, breaking the "model → map → event → model" feedback loop. Dragging or zooming the map keeps the bound values in sync.

```vue [MapBasicExample.vue]
<script setup lang="ts">
const center = ref<[number, number]>([116.397, 39.908])
const zoom = ref(9)
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap
      v-model:center="center"
      v-model:zoom="zoom"
      :options="{ style: 'https://tiles.openfreemap.org/styles/liberty' }"
    >
      <MaplibreNavigationControl position="top-right" />
    </MaplibreMap>
  </div>
</template>
```

## Examples

### Camera Transitions

Use `flyTo` from `useMaplibreCamera` to smoothly animate between multiple preset camera positions:

```vue [MapCameraExample.vue]
<script setup lang="ts">
import type { LngLatLike } from 'maplibre-gl'

const mapId = 'camera-demo'
const { flyTo } = useMaplibreCamera({ mapId })

const presets: { label: string, center: LngLatLike, zoom: number }[] = [
  { label: 'Beijing', center: [116.397, 39.908], zoom: 10 },
  { label: 'Shanghai', center: [121.473, 31.230], zoom: 10 },
  { label: 'Shenzhen', center: [114.057, 22.543], zoom: 10 }
]

function go(center: LngLatLike, zoom: number) {
  flyTo({ center, zoom, duration: 2000 })
}
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap
      :map-id="mapId"
      :options="{ style: 'https://tiles.openfreemap.org/styles/liberty', center: [116.397, 39.908], zoom: 10 }"
    >
      <div class="absolute left-3 top-3 z-10 flex flex-wrap gap-2">
        <UButton
          v-for="p in presets"
          :key="p.label"
          size="xs"
          color="neutral"
          variant="solid"
          @click="go(p.center, p.zoom)"
        >
          {{ p.label }}
        </UButton>
      </div>
    </MaplibreMap>
  </div>
</template>
```

## API

### Props

```ts
/**
 * Props for the MaplibreMap component
 */
interface MaplibreMapProps {
  /**
   * 地图 id；省略时自动生成。提供后可经 useMaplibre(id) 外部访问
   */
  mapId?: string | undefined;
  /**
   * maplibre-gl Map 初始化选项（container 由组件接管）；缺省 style 时使用空白样式。
   * 除 style 外仅在创建时读取，变更需配合 `:key` 重建
   */
  options?: MaplibreMapOptions | undefined;
  /**
   * 卸载时不销毁实例，配合 keepalive / `<keep-alive>` 跨路由复用
   * @default false
   */
  persistent?: boolean | undefined;
  center?: LngLatLike | undefined;
  zoom?: number | undefined;
  bearing?: number | undefined;
  pitch?: number | undefined;
}
```

### Emits

`update:center` / `update:zoom` / `update:bearing` / `update:pitch` are the camera `v-model` sync events. All other events are forwarded maplibre-gl map events.

```ts
/**
 * Emitted events for the MaplibreMap component
 */
interface MaplibreMapEmits {
  click: (payload: [event: MapMouseEvent]) => void;
  contextmenu: (payload: [event: MapMouseEvent]) => void;
  dblclick: (payload: [event: MapMouseEvent]) => void;
  dragend: (payload: [event: MapMovementEvent]) => void;
  error: (payload: [event: ErrorEvent]) => void;
  load: (payload: [map: Map]) => void;
  mousedown: (payload: [event: MapMouseEvent]) => void;
  mousemove: (payload: [event: MapMouseEvent]) => void;
  mouseup: (payload: [event: MapMouseEvent]) => void;
  update:center: (payload: [value: LngLatLike | undefined]) => void;
  update:zoom: (payload: [value: number | undefined]) => void;
  update:bearing: (payload: [value: number | undefined]) => void;
  update:pitch: (payload: [value: number | undefined]) => void;
  idle: (payload: [map: Map]) => void;
  movestart: (payload: [event: MapMovementEvent]) => void;
  moveend: (payload: [event: MapMovementEvent]) => void;
  zoomstart: (payload: [event: MapMovementEvent]) => void;
  zoomend: (payload: [event: MapMovementEvent]) => void;
  rotateend: (payload: [event: MapMovementEvent]) => void;
  pitchend: (payload: [event: MapMovementEvent]) => void;
  styledata: (payload: [event: MapStyleDataEvent]) => void;
  sourcedata: (payload: [event: MapSourceDataEvent]) => void;
}
```

### Slots

```ts
/**
 * Slots for the MaplibreMap component
 */
interface MaplibreMapSlots {
  default(): any;
}
```

## Changelog

See commit history for [src/runtime/components/MaplibreMap.vue](https://github.com/mhaibaraai/movk-maplibre/commits/main/src/runtime/components/MaplibreMap.vue).


## Sitemap

See the full [sitemap](https://maplibre.mhaibaraai.cn/sitemap.md) for all pages.
