---
title: "Tag Group"
description: "A focusable list of tags that can be navigated, selected and removed with the keyboard."
canonical_url: "https://vinicunca.dev/akar/components/tag-group"
---
# Tag Group

> A focusable list of tags that can be navigated, selected and removed with the keyboard.

`````vue [index.vue]
<script setup lang="ts">
import { TagGroupItem, TagGroupItemDelete, TagGroupItemText, TagGroupRoot } from 'akar'
import { ref } from 'vue'

const tags = ref(['Apple', 'Banana', 'Blueberry', 'Grape'])
const selected = ref<string[]>(['Banana'])

function onRemove(values: string[]) {
  tags.value = tags.value.filter(tag => !values.includes(tag))
}
</script>

<template>
  <div class="akar-demo akar-demo-TagGroup">
    <TagGroupRoot
        v-model="selected"
        selection-mode="multiple"
        aria-label="Fruits"
        class="flex w-[380px] flex-wrap items-center gap-2"
        @remove="onRemove"
      >
        <TagGroupItem
          v-for="tag in tags"
          :key="tag"
          :value="tag"
          class="flex cursor-default items-center justify-center gap-1.5 rounded-lg bg-white px-2 py-1 text-sm font-medium text-grass11 shadow-md outline-none data-[state=checked]:bg-grass9 data-[state=checked]:text-white data-[disabled]:opacity-50 focus-visible:ring-2 focus-visible:ring-grass7"
        >
          <TagGroupItemText>{{ tag }}</TagGroupItemText>
          <TagGroupItemDelete
            class="rounded bg-transparent p-0.5 hover:bg-blackA4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-grass7"
          >
            <PIcon
              name="i-lucide-x"
              class="size-3"
            />
          </TagGroupItemDelete>
        </TagGroupItem>
        <span
          v-if="!tags.length"
          class="text-sm text-mauve11"
        >No fruits</span>
      </TagGroupRoot>
  </div>
</template>
```

```css [styles.css]
@import '../_colors/black-alpha.css';
@import '../_colors/mauve.css';
@import '../_colors/grass.css';

@scope (.akar-demo-TagGroup) {
/* reset */
button {
  all: unset;
}
}
```

## Features

- Full keyboard navigation, including typeahead.
- Supports no, single or multiple selection.
- Removable with the keyboard or a delete button.
- Keeps focus on a neighbouring tag after removal.
- Can be controlled or uncontrolled.
- Labelled and described by an ancestor Field.

## Installation

Install the component from your command line.

```bash [pnpm]
pnpm add akar
```

```bash [yarn]
yarn add akar
```

```bash [npm]
npm install akar
```

```bash [bun]
bun add akar
```

## Anatomy

Import all parts and piece them together.

```vue
<script setup>
import { TagGroupItem, TagGroupItemDelete, TagGroupItemText, TagGroupRoot } from 'akar';
</script>

<template>
  <TagGroupRoot>
    <TagGroupItem>
      <TagGroupItemText />
      <TagGroupItemDelete />
    </TagGroupItem>
  </TagGroupRoot>
</template>
```

## API Reference

### Root

Contains all the tags. Renders a `grid`, or a `group` when there are no tags.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](/akar/guides/composition) guide for more details. |
| `by` |  | `string \| ((a: T, b: T) => boolean)` | Use this to compare objects by a particular field, or pass your own comparison function for complete control over how objects are compared. |
| `defaultValue` |  | `T \| T[]` | The value of the selected tag(s) when initially rendered. Use when you do not need to control the selection. |
| `dir` |  | `"ltr" \| "rtl"` | The reading direction of the tag group when applicable.<br><br>If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
| `disabled` |  | `boolean` | When `true`, prevents the user from interacting with the tag group and all its tags. |
| `disallowEmptySelection` |  | `boolean` | When `true`, the user cannot deselect the last selected tag. |
| `escapeKeyBehavior` | `"clearSelection"` | `"none" \| "clearSelection"` | Whether pressing <kbd>Escape</kbd> clears the selection. |
| `loop` | `true` | `boolean` | When `true`, keyboard navigation will loop from last tag to first, and vice versa. |
| `modelValue` |  | `T \| T[]` | The controlled value of the selected tag(s). Can be binded with `v-model`. An array when `selectionMode` is `multiple`. |
| `selectionMode` | `"none"` | `"single" \| "multiple" \| "none"` | The type of selection that is allowed. Tags are not selectable when `none`. |

**Events**

| Event | Payload | Description |
| --- | --- | --- |
| `remove` | `values: T[]` | Event handler called when the user removes tags, with the values to remove.<br>Tags are only removable when this event has a listener; remove the values from your own list to remove the tags. |
| `update:modelValue` | `value: T \| T[]` | Event handler called when the selection changes. |

**Slots**

| Slot | Payload | Description |
| --- | --- | --- |
| `modelValue` | `T \| T[]` | Current selected value(s) |

[TagGroupRoot.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/TagGroup/TagGroupRoot.vue)

| Data attribute | Value |
| --- | --- |
| `[data-disabled]` | Present when disabled |
| `[data-empty]` | Present when there are no tags |

### Item

A tag. Pressing `Delete` or `Backspace` on it removes it.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](/akar/guides/composition) guide for more details. |
| `disabled` |  | `boolean` | When `true`, prevents the user from interacting with the tag. |
| `textValue` |  | `string` | A string representation of the tag's contents, used for typeahead and as the tag's accessible name. Defaults to the text of `TagGroupItemText`. |
| `value*` |  | `T` | The unique value of the tag. |

**Slots**

| Slot | Payload | Description |
| --- | --- | --- |
| `selected` | `boolean` | Whether the tag is selected |
| `disabled` | `boolean` | Whether the tag is disabled |

[TagGroupItem.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/TagGroup/TagGroupItem.vue)

| Data attribute | Value |
| --- | --- |
| `[data-state]` | "checked" \\| "unchecked" |
| `[data-disabled]` | Present when disabled |

### ItemText

The text of the tag. Used for typeahead unless the item has a `textValue`.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"span"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](/akar/guides/composition) guide for more details. |

[TagGroupItemText.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/TagGroup/TagGroupItemText.vue)

### ItemDelete

The button that removes its tag. It is labelled "Remove" followed by the tag's name; pass an `aria-label` to translate it.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"button"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](/akar/guides/composition) guide for more details. |
| `disabled` |  | `boolean` | When `true`, prevents the user from interacting with the delete button. |

[TagGroupItemDelete.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/TagGroup/TagGroupItemDelete.vue)

| Data attribute | Value |
| --- | --- |
| `[data-disabled]` | Present when disabled |

## Examples

### Removing tags

Tags are removable when you listen to the `remove` event. It receives the values to remove, which you remove from your own list. Removing a selected tag with the keyboard removes every selected tag.

```vue
<script setup>
import { ref } from 'vue'

const tags = ref(['News', 'Travel', 'Gaming'])
function onRemove(values) {
  tags.value = tags.value.filter(tag => !values.includes(tag))
}
</script>

<template>
  <TagGroupRoot aria-label="Categories" @remove="onRemove">
    <TagGroupItem v-for="tag in tags" :key="tag" :value="tag">
      <TagGroupItemText>{{ tag }}</TagGroupItemText>
      <TagGroupItemDelete>×</TagGroupItemDelete>
    </TagGroupItem>
  </TagGroupRoot>
</template>
```

### Selection

Set `selectionMode` to `single` or `multiple` to make tags selectable with a click, `Space` or `Enter`. `v-model` holds the selected value, or an array of values when `multiple`.

```vue
<template>
  <TagGroupRoot v-model="selected" selection-mode="multiple" aria-label="Amenities">
    <TagGroupItem v-for="amenity in amenities" :key="amenity" :value="amenity">
      <TagGroupItemText>{{ amenity }}</TagGroupItemText>
    </TagGroupItem>
  </TagGroupRoot>
</template>
```

### Empty state

When there are no tags, the root renders as a focusable `group` with a `data-empty` attribute. Render any empty content inside it.

```vue
<template>
  <TagGroupRoot aria-label="Categories" @remove="onRemove">
    <TagGroupItem v-for="tag in tags" :key="tag" :value="tag">
      <TagGroupItemText>{{ tag }}</TagGroupItemText>
    </TagGroupItem>
    <span v-if="!tags.length">No categories
  </TagGroupRoot>
</template>
```

### With a label and description

Place the root in a `FieldRoot` to label and describe it.

```vue
<template>
  <FieldRoot>
    <FieldLabel :native-label="false">
      Categories
    </FieldLabel>
    <TagGroupRoot @remove="onRemove">
      <!-- ... -->
    </TagGroupRoot>
    <FieldDescription>Your selected categories.</FieldDescription>
  </FieldRoot>
</template>
```

## Accessibility

Adheres to the [Grid WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/), with each tag a `row`. When using `asChild` on `TagGroupItem`, wrap the tag's content in an element with `role="gridcell"`.

### Keyboard Interactions

| Key | Description |
| --- | --- |
| <kbd>Tab</kbd> | Moves focus into the tag group, to the focused tag's delete button, then out of the tag group. |
| <kbd>ArrowRight</kbd> / <kbd>ArrowDown</kbd> | Moves focus to the next tag. |
| <kbd>ArrowLeft</kbd> / <kbd>ArrowUp</kbd> | Moves focus to the previous tag. |
| <kbd>Home</kbd> / <kbd>End</kbd> | Moves focus to the first or last tag. |
| <kbd>Space</kbd> / <kbd>Enter</kbd> | When selectable, toggles the focused tag's selection. |
| <kbd>Delete</kbd> / <kbd>Backspace</kbd> | Removes the focused tag, or every selected tag when the focused tag is selected. |
| <kbd>Escape</kbd> | Clears the selection. |

---

- [GitHub](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/TagGroup)
- [npm](https://www.npmjs.com/package/akar)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
