---
title: "MaplibreLayerGroup"
description: "A layer group that shares an insertion anchor, visibility and opacity across child layers, and feeds the layer control and legend when titled."
canonical_url: "https://maplibre.mhaibaraai.cn/en/docs/core/layer-group"
---
# MaplibreLayerGroup

> A layer group that shares an insertion anchor, visibility and opacity across child layers, and feeds the layer control and legend when titled.

## Introduction

`MaplibreLayerGroup` groups several child [MaplibreLayer](https://maplibre.mhaibaraai.cn/docs/core/layer) components and is the single source of truth for layer management:

- **Insertion anchor**: `beforeId` sets the default insertion position for layers in the group, inherited from the parent group when unset.
- **Visibility and opacity**: `v-model:visible` and `v-model:opacity` apply to every layer in the group. Hiding the group takes precedence over each layer's own `layout.visibility`; opacity scales the opacity properties of each layer type and multiplies with the layer's own values.
- **Nesting**: nested groups combine visibility with AND and multiply opacity.
- **Layer tree**: with a `title`, the group appears in [MaplibreLayerControl](https://maplibre.mhaibaraai.cn/docs/controls/layer-control), [MaplibreLegend](https://maplibre.mhaibaraai.cn/docs/controls/legend) and [useLayerTree](https://maplibre.mhaibaraai.cn/docs/composables/use-layer-tree), all of which read and write this group's `v-model`.
- **Basemap layers**: the `styleLayers` predicate brings layers that ship with the basemap style (labels, roads and so on) under the group's control.

> [!NOTE]
> 
> Opacity expressions driven by zoom (
> 
> interpolate
> 
>  / 
> 
> step
> 
>  with 
> 
> zoom
> 
>  input) are scaled output by output; expressions that cannot be scaled safely, such as 
> 
> zoom
> 
>  used below the top level, keep their value and log a warning.

## Usage

Toggling `visible` shows or hides the entire group — no need to update each layer individually:

```vue [LayerGroupExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'

const visible = ref(true)

const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    properties: {},
    geometry: {
      type: 'Polygon',
      coordinates: [[[116.36, 39.95], [116.44, 39.95], [116.44, 39.89], [116.36, 39.89], [116.36, 39.95]]]
    }
  }]
}
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/positron', center: [116.4, 39.92], zoom: 11 }">
      <div class="absolute left-3 top-3 z-10">
        <UButton size="xs" color="neutral" variant="solid" @click="visible = !visible">
          {{ visible ? 'Hide group' : 'Show group' }}
        </UButton>
      </div>
      <!-- 组级 visible 统一开关子图层，无需逐层设置 layout.visibility -->
      <MaplibreLayerGroup :visible="visible">
        <MaplibreLayer
          layer-id="zone-fill"
          type="fill"
          :source="{ type: 'geojson', data }"
          :paint="{ 'fill-color': '#f43f5e', 'fill-opacity': 0.3 }"
        />
        <MaplibreLayer
          layer-id="zone-line"
          type="line"
          :source="{ type: 'geojson', data }"
          :paint="{ 'line-color': '#f43f5e', 'line-width': 2 }"
        />
      </MaplibreLayerGroup>
    </MaplibreMap>
  </div>
</template>
```

## Examples

### Two-way visibility and opacity

The external switch, slider and layer control write back to the same `v-model`, so an action in one place updates the others:

```vue [LayerGroupOpacityExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'

const visible = ref(true)
const opacity = ref(1)

const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[[116.36, 39.95], [116.44, 39.95], [116.44, 39.89], [116.36, 39.89], [116.36, 39.95]]] } }]
}
</script>

<template>
  <div class="relative h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/positron', center: [116.4, 39.92], zoom: 11 }">
      <!-- v-model 双向绑定：外部按钮与图层控件写回同一份状态 -->
      <MaplibreLayerGroup v-model:visible="visible" v-model:opacity="opacity" title="规划片区">
        <MaplibreLayer layer-id="opacity-fill" type="fill" :source="{ type: 'geojson', data }" :paint="{ 'fill-color': '#f43f5e', 'fill-opacity': 0.5 }" />
        <MaplibreLayer layer-id="opacity-line" type="line" :source="{ type: 'geojson', data }" :paint="{ 'line-color': '#f43f5e', 'line-width': 3 }" />
      </MaplibreLayerGroup>
      <MaplibreLayerControl position="top-right" />
    </MaplibreMap>
    <div class="absolute left-3 top-3 z-10 flex w-48 flex-col gap-2 rounded-md bg-default/90 p-2 ring ring-default">
      <USwitch v-model="visible" label="显示" size="sm" />
      <USlider
        v-model="opacity"
        :min="0"
        :max="1"
        :step="0.05"
        size="sm"
        :disabled="!visible"
      />
    </div>
  </div>
</template>
```

### Control basemap layers

`styleLayers` matches layers that ship with the basemap style by their traits (type, `source-layer` and so on) rather than hard-coded ids, so it keeps working with another style:

- Only layers present when the style loads are matched; business layers added at runtime are unaffected even if they fit the predicate.
- After a basemap switch the group matches the new style again and reapplies its state.
- When the group unmounts, adopted layers return to their original visibility and opacity.

Colors of such layers are mostly zoom-driven expressions, so the derived legend is meaningless; set `:legend="[]"` in most cases.

> [!NOTE]
> 
> On raster basemaps (such as 
> 
> Tianditu
> 
> ) the annotation is a separate raster layer and must be matched by layer id; features drawn into the basemap tiles, such as roads, cannot be split out.

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

// 按图层特征匹配，而非写死 id：换一套样式同样适用
const isLabel = (layer: LayerSpecification) => layer.type === 'symbol'
const isRoad = (layer: LayerSpecification) => 'source-layer' in layer && layer['source-layer'] === 'transportation'
const isBuilding = (layer: LayerSpecification) => 'source-layer' in layer && layer['source-layer'] === 'building'
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MaplibreMap :options="{ style: 'https://tiles.openfreemap.org/styles/liberty', center: [116.397, 39.908], zoom: 14 }">
      <MaplibreLayerGroup title="注记" :style-layers="isLabel" :legend="[]" />
      <MaplibreLayerGroup title="道路" :style-layers="isRoad" :legend="[]" />
      <MaplibreLayerGroup title="建筑" :style-layers="isBuilding" :legend="[]" />
      <MaplibreLayerControl position="top-left" />
    </MaplibreMap>
  </div>
</template>
```

## API

### Props

```ts
/**
 * Props for the MaplibreLayerGroup component
 */
interface MaplibreLayerGroupProps {
  /**
   * 组内图层缺省插入到该图层之前；未设置时继承父组
   */
  beforeId?: string | undefined;
  /**
   * 组标题；设置后注册到图层树，出现在 MaplibreLayerControl、MaplibreLegend 与 useLayerTree 中
   */
  title?: string | undefined;
  /**
   * 图例项；省略时由子图层颜色推导
   */
  legend?: LegendItem[] | undefined;
  /**
   * 认领底图样式自带的图层（按类型、source-layer 等匹配），与子图层一起受组的显隐与透明度控制
   */
  styleLayers?: (layer: LayerSpecification): boolean | undefined;
  /**
   * 组级显隐（组开关优先于子图层自身的 layout.visibility）
   * @default true
   */
  visible?: boolean | undefined;
  /**
   * 组级透明度 0..1，按图层类型缩放各透明度属性
   * @default 1
   */
  opacity?: number | undefined;
}
```

### Emits

```ts
/**
 * Emitted events for the MaplibreLayerGroup component
 */
interface MaplibreLayerGroupEmits {
  update:visible: (payload: [value: boolean]) => void;
  update:opacity: (payload: [value: number]) => void;
}
```

### Slots

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

## Changelog

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


## Sitemap

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