---
title: "MapboxMarker"
description: "A marker that renders custom DOM through its default slot, with v-model:lnglat two-way binding, drag-to-update support and a built-in popup."
canonical_url: "https://mapbox.mhaibaraai.cn/en/docs/core/marker"
---
# MapboxMarker

> A marker that renders custom DOM through its default slot, with v-model:lnglat two-way binding, drag-to-update support and a built-in popup.

## Introduction

`MapboxMarker` places a marker on the map: the default slot provides custom DOM content (omitting it falls back to the mapbox default teardrop icon), and the position is two-way bound via `v-model:lnglat`. When `options.draggable` is enabled, the new coordinates are written back to the bound value after each drag ends.

Provide a `#popup` slot to attach a popup rendering any Vue component. `trigger` controls how it opens (`click` / `hover` / `none`), and the open state is exposed through `v-model:open`.

## Usage

Without a slot, the default marker is used and `v-model:lnglat` controls the position:

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

const position = ref<LngLatLike>([116.397, 39.908])
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/streets-v12', center: [116.397, 39.908], zoom: 12 }">
      <!-- 无插槽时使用 mapbox 默认水滴标记 -->
      <MapboxMarker v-model:lnglat="position" />
    </MapboxMap>
  </div>
</template>
```

## Examples

### Custom Content and Dragging

The default slot renders custom DOM. With `draggable` enabled, coordinates are written back in real time as the marker is dragged:

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

const position = ref<LngLatLike>([116.397, 39.908])

const label = computed(() => {
  const [lng, lat] = position.value as [number, number]
  return `${lng.toFixed(3)}, ${lat.toFixed(3)}`
})
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/streets-v12', center: [116.397, 39.908], zoom: 12 }">
      <!-- 默认插槽自定义 DOM；draggable 时拖拽结束回写 v-model:lnglat -->
      <MapboxMarker v-model:lnglat="position" :options="{ draggable: true }">
        <div class="flex size-8 items-center justify-center rounded-full border-2 border-white bg-primary text-white shadow-lg">
          <UIcon name="i-lucide-map-pin" class="size-4" />
        </div>
      </MapboxMarker>
      <div class="absolute left-3 top-3 z-10 rounded-(--ui-radius) border border-default bg-default/80 px-3 py-1.5 text-sm backdrop-blur">
        拖拽标记：{{ label }}
      </div>
    </MapboxMap>
  </div>
</template>
```

### Click to Open Custom Content `v1.2.0+`

With a `#popup` slot provided, clicking the marker toggles the popup. The slot exposes `close` so the content can dismiss itself:

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

const position = ref<LngLatLike>([116.397, 39.908])
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/streets-v12', center: [116.397, 39.908], zoom: 13 }">
      <MapboxMarker :lnglat="position" :popup-options="{ offset: 20 }">
        <div class="flex size-8 cursor-pointer items-center justify-center rounded-full border-2 border-white bg-primary text-white shadow-lg">
          <UIcon name="i-lucide-map-pin" class="size-4" />
        </div>
        <template #popup="{ close }">
          <div class="w-48 px-1 py-0.5">
            <p class="font-semibold">
              天安门
            </p>
            <p class="mt-0.5 text-sm text-muted">
              北京市东城区长安街
            </p>
            <UButton class="mt-2" size="xs" color="neutral" variant="subtle" @click="close">
              知道了
            </UButton>
          </div>
        </template>
      </MapboxMarker>
    </MapboxMap>
  </div>
</template>
```

> [!NOTE]
> 
> With 
> 
> trigger="click"
> 
> , the click on the marker does not bubble to the map, so 
> 
> MapboxMap
> 
>  emits no 
> 
> click
> 
>  event for it. This is required: marker elements live inside the map's canvas container, and letting the click bubble would fire mapbox's 
> 
> preclick
> 
> , closing the just-opened popup via 
> 
> closeOnClick
> 
> .

> [!NOTE]
> 
> With 
> 
> trigger="hover"
> 
> , moving the cursor into the popup fires the marker's 
> 
> mouseleave
> 
>  and closes it — an inherent limitation of the popup and marker being separate DOM nodes. For hover, prefer a small 
> 
> popupOptions.offset
> 
> ; if the popup needs interaction, use 
> 
> click
> 
> .

### A Group of Markers Open by Default `v1.2.0+`

Each marker owns its own `open` state, so a static `:open="true"` opens it by default. They are independent of one another and can still be toggled individually:

```vue [MarkerGroupPopupExample.vue]
<script setup lang="ts">
interface Poi {
  id: string
  lnglat: [number, number]
  name: string
  type: string
}

const points: Poi[] = [
  { id: 'tam', lnglat: [116.397, 39.908], name: '天安门', type: '地标' },
  { id: 'gm', lnglat: [116.461, 39.909], name: '国贸', type: '商圈' },
  { id: 'zgc', lnglat: [116.316, 39.983], name: '中关村', type: '科技园' }
]
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/light-v11', center: [116.39, 39.94], zoom: 10.6 }">
      <MapboxMarker
        v-for="point in points"
        :key="point.id"
        :lnglat="point.lnglat"
        :open="true"
        :popup-options="{ offset: 18, closeButton: false, closeOnClick: false }"
      >
        <div class="size-3 cursor-pointer rounded-full border-2 border-white bg-primary shadow" />
        <template #popup>
          <div class="px-1 text-sm">
            <span class="font-semibold">{{ point.name }}</span>
            <span class="text-muted"> · {{ point.type }}</span>
          </div>
        </template>
      </MapboxMarker>
    </MapboxMap>
  </div>
</template>
```

> [!TIP]
> 
> To keep only one popup open at a time, hold a single 
> 
> activeId
> 
>  outside and bind 
> 
> v-model:open
> 
> . For hundreds of points, render labels with a symbol layer's 
> 
> text-field
> 
>  instead of mounting N popup DOM nodes.

## API

### Props

```ts
/**
 * Props for the MapboxMarker component
 */
interface MapboxMarkerProps {
  lnglat: mapboxgl.LngLatLike;
  /**
   * 标记选项；element 由默认插槽提供，无需在此传入
   */
  options?: Omit<mapboxgl.MarkerOptions, "element"> | undefined;
  /**
   * 弹窗选项，仅在提供 #popup 插槽时生效
   */
  popupOptions?: mapboxgl.PopupOptions | undefined;
  /**
   * #popup 插槽的触发时机；'none' 表示不绑定监听，完全由 v-model:open 受控
   * @default "\"click\""
   */
  trigger?: PopupTrigger | undefined;
  /**
   * 弹窗开合状态；初始传 true 即默认展开
   * @default "false"
   */
  open?: boolean | undefined;
}
```

### Emits

```ts
/**
 * Emitted events for the MapboxMarker component
 */
interface MapboxMarkerEmits {
  update:lnglat: (payload: [value: mapboxgl.LngLatLike]) => void;
  update:open: (payload: [value: boolean]) => void;
}
```

### Slots

```ts
/**
 * Slots for the MapboxMarker component
 */
interface MapboxMarkerSlots {
  popup(): any;
  default(): any;
}
```

### Expose

Access the component instance via [`useTemplateRef`](https://vuejs.org/api/composition-api-helpers.html#usetemplateref).

<table>
<thead>
  <tr>
    <th>
      Name
    </th>
    
    <th>
      Type
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" language="ts-type" style="">
        <span class="sBMFI">
          marker
        </span>
      </code>
    </td>
    
    <td>
      <code className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" language="ts-type" style="">
        <span class="sMK4o">
          ()
        </span>
        
        <span class="spNyl">
          =>
        </span>
        
        <span class="sBMFI">
          Marker
        </span>
        
        <span class="sMK4o">
          |
        </span>
        
        <span class="sBMFI">
          undefined
        </span>
      </code>
      
       <br />
      
       <p>
        Returns the underlying mapbox-gl Marker instance
      </p>
    </td>
  </tr>
</tbody>
</table>

## Changelog

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


## Sitemap

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