Skip to content
RTL Support: Full
Accessibility: Full
Translations: Not Needed

Tag Group

Tag Group is a component that allows you to create a list of tags.

Basic

A list of tags initialized from a default value array, with add and remove support.

Blogs
Websites
Funnels
+0
vue
<template>
  <div style="width: 500px; border: 1px solid #ccc; padding: 4px">
    <HLTagGroup id="demo" :defaultValue="defaultValuesString" @update:value="handleChange" />
  </div>
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
  const defaultValuesString = ['Blogs', 'Websites', 'Funnels']
  const handleChange = value => {
    console.log(value)
  }
</script>

Sizes

Use size to scale every tag in the group. Supported values are lg (default), md, sm, and xs.

Blogs
Websites
Funnels
+0
Blogs
Websites
Funnels
+0
Blogs
Websites
Funnels
+0
Blogs
Websites
Funnels
+0
vue
<template>
  <HLTagGroup id="size-demo-lg" :default-value="tags" size="lg" />
  <HLTagGroup id="size-demo-md" :default-value="tags" size="md" />
  <HLTagGroup id="size-demo-sm" :default-value="tags" size="sm" />
  <HLTagGroup id="size-demo-xs" :default-value="tags" size="xs" />
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'

  const tags = ['Blogs', 'Websites', 'Funnels']
</script>

With Popover

Renders a popover slot listing avatar options that can be filtered on input and selected into the tag group.

Nico Robin
Zenin Toji
Itadori Yuuji
Gojo Satoru
+0
vue
<template>
  <div style="width: 500px; border: 1px solid #ccc; padding: 8px">
    <HLTagGroup ref="tagGroupRef" @update:value="handleChange" :default-value="defaultValues" @input="handleInput" size="sm">
      <template #popover-content>
        <div class="p-2">
          <div v-if="popoverAvatarOptions.length === 0">
            <HLText>No options found</HLText>
          </div>
          <div
            v-else
            v-for="option in popoverAvatarOptions"
            :key="option.name"
            @click="handlePopoverClick(option)"
            class="cursor-pointer flex items-center gap-2 hover:bg-gray-100 px-1"
          >
            <HLAvatar v-bind="option" size="3xs" />
            <span>{{ option.name }}</span>
          </div>
        </div>
      </template>
    </HLTagGroup>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLTagGroup, HLAvatar, HLText } from '@platform-ui/highrise'

const defaultValues = ['Nico Robin', 'Zenin Toji', 'Itadori Yuuji', 'Gojo Satoru']

const options = [
  { name: 'Zenin Toji', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Toji', objectFit: 'cover', size: 'xs', color: 'red', border: true },
  { name: 'Itadori Yuuji', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Yuuji', objectFit: 'cover', size: 'xs', color: 'green', border: true },
  { name: 'Gojo Satoru', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Gojo', objectFit: 'cover', size: 'xs', color: 'blue', border: true },
]

const tagGroupRef = ref(null)
const popoverAvatarOptions = ref(options)

const handleInput = (filterString = '') => {
  popoverAvatarOptions.value = options.filter(option => option.name.toLowerCase().includes(filterString.toLowerCase()))
}
const handleChange = (value: string[]) => {
  console.log(value)
}
const handlePopoverClick = (option: { name: string }) => {
  tagGroupRef.value?.setInputValue(option)
}
</script>

With Popover and custom render

Uses the customRender and customRenderAvatar slots to control how each tag and its avatar are displayed.

Nico Robin
Zenin Toji
Itadori Yuuji
+0
vue
<template>
  <div style="width: 400px; border: 1px solid #ccc; padding: 8px">
    <HLTagGroup ref="tagGroupRef" @update:value="handleChange" :default-value="defaultValues" @input="handleInput" size="sm">
      <template #popover-content>
        <div class="p-2">
          <div v-if="popoverAvatarOptions.length === 0">
            <HLText>No options found</HLText>
          </div>
          <div
            v-else
            v-for="option in popoverAvatarOptions"
            :key="option.name"
            @click="handlePopoverClick(option)"
            class="cursor-pointer flex items-center gap-2 hover:bg-gray-100 px-1"
          >
            <HLAvatar v-bind="option" size="3xs" />
            <span>{{ option.name }}</span>
          </div>
        </div>
      </template>
      <template #customRender="{ tag, index }">
        <span>{{ tag.name }}</span>
      </template>
      <template #customRenderAvatar="{ tag, index }">
        <HLAvatar v-bind="tag" />
      </template>
    </HLTagGroup>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLTagGroup, HLAvatar, HLText } from '@platform-ui/highrise'

// Seeds the tags shown in the group
const defaultValues = [
  { name: 'Nico Robin', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Robin', objectFit: 'cover' },
  { name: 'Zenin Toji', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Toji', objectFit: 'cover' },
  { name: 'Itadori Yuuji', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Yuuji', objectFit: 'cover' },
]

// Selectable options listed in the popover
const options = [
  { name: 'Zenin Toji', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Toji', objectFit: 'cover', size: 'xs', color: 'red', border: true },
  { name: 'Itadori Yuuji', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Yuuji', objectFit: 'cover', size: 'xs', color: 'green', border: true },
  { name: 'Gojo Satoru', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Gojo', objectFit: 'cover', size: 'xs', color: 'blue', border: true },
]

const tagGroupRef = ref(null)
const popoverAvatarOptions = ref(options)

const handleInput = (filterString = '') => {
  popoverAvatarOptions.value = options.filter(option => option.name.toLowerCase().includes(filterString.toLowerCase()))
}
const handleChange = (value: string[]) => {
  console.log(value)
}
const handlePopoverClick = (option: { name: string }) => {
  tagGroupRef.value?.setInputValue(option)
}
</script>

Tag Truncation

When tags have long labels, you can enable truncation to prevent them from overflowing their container. This is particularly useful when dealing with lengthy tag names.

Very Long Tag Name That Should Be Truncated
Another Extremely Long Tag Label
Short Tag
Yet Another Long Tag Name That Exceeds The Container Width
+0
vue
<template>
  <div style="width: 400px; border: 1px solid #ccc; padding: 8px">
    <HLTagGroup id="truncated-demo" :default-value="longTags" :truncate="true" :max-width="100" @update:value="handleChange" />
  </div>
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'

  const longTags = [
    'Very Long Tag Name That Should Be Truncated',
    'Another Extremely Long Tag Label',
    'Short Tag',
    'Yet Another Long Tag Name That Exceeds The Container Width',
  ]

  const handleChange = value => {
    console.log(value)
  }

</script>

Max Visible Tags

max controls how many tags stay visible before the rest collapse into a +N counter. Hover the counter to see the hidden tags in a tooltip.

It accepts two forms:

  • 'responsive' (default) — fit as many tags as the container width allows, recalculating when the container resizes.
  • A number — always show exactly that many tags, regardless of available width.

max="responsive" — fills the container

Blogs
Websites
Funnels
Forms
Surveys
Calendars
Campaigns
+0

:max="3" — always three tags

Blogs
Websites
Funnels

:max="1" — a single tag plus the counter

Blogs
vue
<template>
  <!-- Fit as many tags as the width allows (default) -->
  <HLTagGroup id="max-demo-responsive" :default-value="tags" max="responsive" />

  <!-- Always show exactly 3, collapse the rest into +N -->
  <HLTagGroup id="max-demo-3" :default-value="tags" :max="3" />
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'

  const tags = ['Blogs', 'Websites', 'Funnels', 'Forms', 'Surveys', 'Calendars', 'Campaigns']
</script>

INFO

@update:counter and @update:overflow only fire when max is a number. Use tooltipVariant to theme the counter's tooltip — see Tooltip Variant.

Color

Use the color prop to apply a single HLTag color to every tag in the group. Defaults to gray.

Blogs
Websites
Funnels
+0
Vue
html
<template>
  <HLTagGroup id="color-demo" :default-value="['Blogs', 'Websites', 'Funnels']" color="primary" />
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
</script>

Custom Colors

Provide a customColors array to color tags individually. Colors are applied by tag index and cycle when there are more tags than colors. customColors takes precedence over color.

Nico Robin
Zenin Toji
Itadori Yuuji
Gojo Satoru
+0
Vue
html
<template>
  <HLTagGroup
    id="custom-colors-demo"
    :default-value="['Nico Robin', 'Zenin Toji', 'Itadori Yuuji', 'Gojo Satoru']"
    :custom-colors="['primary', 'success', 'warning', 'purple']"
  />
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
</script>

Bordered

Set bordered to render tags with a border instead of a solid fill.

Blogs
Websites
Funnels
+0
Vue
html
<template>
  <HLTagGroup id="bordered-demo" :default-value="['Blogs', 'Websites', 'Funnels']" color="primary" :bordered="true" />
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
</script>

Tooltip Variant

When tags overflow, the +N counter reveals the hidden tags in a tooltip. Use tooltipVariant to switch that tooltip between dark (default) and light.

Nico Robin
Zenin Toji
Itadori Yuuji
Gojo Satoru
+0
Vue
html
<template>
  <!-- Narrow container forces overflow so the counter tooltip appears -->
  <div style="width: 240px">
    <HLTagGroup
      id="tooltip-variant-demo"
      :default-value="['Nico Robin', 'Zenin Toji', 'Itadori Yuuji', 'Gojo Satoru']"
      tooltip-variant="light"
    />
  </div>
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
</script>

Disabled

A non-interactive tag group; tags cannot be added or removed while disabled is set.

Blogs
Websites
Funnels
vue
<template>
  <HLTagGroup
    id="demo"
    :default-value="defaultValue"
    :max="5"
    :disabled="true"
    :closable="true"
    :round="true"
    @update:value="handleChange"
  />
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'

  const defaultValue = ['Blogs', 'Websites', 'Funnels']
  const handleChange = value => {
    console.log(value)
  }
</script>

Delimiters

Use delimiter to choose which keys commit the typed text as a new tag. Here space, comma, and enter all add a tag.

Blogs
+0
vue
<template>
  <div style="width: 500px; border: 1px solid #ccc; padding: 4px">
    <HLTagGroup id="delimiter-demo" :default-value="['Blogs']" :delimiter="['space', 'comma', 'enter']" @update:value="handleChange" />
  </div>
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
  const handleChange = value => {
    console.log(value)
  }
</script>

Non-editable (canAddTag)

Set :canAddTag="false" to hide the text input so existing tags can be removed but no new ones added.

Blogs
Websites
Funnels
+0
vue
<template>
  <div style="width: 500px; border: 1px solid #ccc; padding: 4px">
    <HLTagGroup id="can-add-tag-demo" :default-value="defaultValue" :canAddTag="false" @update:value="handleChange" />
  </div>
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
  const defaultValue = ['Blogs', 'Websites', 'Funnels']
  const handleChange = value => {
    console.log(value)
  }
</script>

Non-interactive

Set :interactive="false" to render the tags without hover effects or a pointer cursor — useful for read-only display.

Blogs
Websites
Funnels
+0
vue
<template>
  <div style="width: 500px; border: 1px solid #ccc; padding: 4px">
    <HLTagGroup id="interactive-demo" :default-value="defaultValue" :interactive="false" :canAddTag="false" @update:value="handleChange" />
  </div>
</template>
<script setup>
  import { HLTagGroup } from '@platform-ui/highrise'
  const defaultValue = ['Blogs', 'Websites', 'Funnels']
  const handleChange = value => {
    console.log(value)
  }
</script>

Event Testing

This example logs the events the tag group emits as you interact with it. Try the following:

  • Type text and press Enter to add a tag (@update:value, @input)
  • Click a tag's close icon to remove it (@close, @update:value)
  • Click a tag to test @click

Because max is a number here, the group collapses extra tags into a counter and fires @update:counter / @update:overflow as the visible set changes.

Blogs
Websites
Funnels
Forms

Event Log:

No events logged yet. Interact with the tags above.
vue
<template>
  <div style="width: 500px; border: 1px solid #ccc; padding: 4px">
    <HLTagGroup
      id="tag-group-events"
      :default-value="tags"
      :max="4"
      @update:value="val => addEventLog('@update:value → ' + JSON.stringify(val))"
      @input="val => addEventLog('@input → ' + val)"
      @close="payload => addEventLog('@close → index ' + payload.index)"
      @click="payload => addEventLog('@click → index ' + payload.index)"
      @update:counter="count => addEventLog('@update:counter → ' + count)"
      @update:overflow="overflow => addEventLog('@update:overflow → ' + overflow)"
    />
  </div>
  <div class="text-sm">
    <p class="font-bold mb-2">Event Log:</p>
    <div v-if="eventLog.length === 0" class="text-gray-500">No events logged yet. Interact with the tags above.</div>
    <div v-for="(log, index) in eventLog" :key="index" class="text-gray-700">{{ log.timestamp }}: {{ log.event }}</div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLTagGroup } from '@platform-ui/highrise'

const tags = ['Blogs', 'Websites', 'Funnels', 'Forms', 'Surveys', 'Calendars', 'Campaigns']
const eventLog = ref<{ event: string; timestamp: string }[]>([])
const addEventLog = (event: string) => {
  eventLog.value.unshift({ event, timestamp: new Date().toLocaleTimeString() })
  if (eventLog.value.length > 5) {
    eventLog.value.pop()
  }
}
</script>

Import

ts
import { HLTagGroup } from '@platform-ui/highrise'

Accessibility

  • Describe the collection with aria-label / aria-labelledby.
  • Update each tag’s aria-pressed / aria-selected so assistive tech hears which values are active.
  • Link helper text (such as max selections) via aria-describedby on the group.

Props

NameTypeDefaultDescription
idstring-The id of the dynamic tags
defaultValuearray-The default value of the dynamic tags
maxnumber | 'responsive'responsiveThe max number of the dynamic tags
type'default' | 'error' | 'primary' | 'info' | 'success' | 'warning''default'The type of the dynamic tags
size'xs' | 'sm' | 'md' | 'lg''lg'The size of the dynamic tags
disabledbooleanfalseWhether the dynamic tags is disabled
closablebooleantrueWhether the dynamic tags is closable
roundbooleantrueWhether the dynamic tags is round
canAddTagbooleantrueWhether the dynamic tags can add tag
delimiterArray<'space' | 'comma' | 'enter'>['enter']Whether the dynamic tags can add tag on comma or space or enter
truncatebooleanfalseWhether to truncate tag text with ellipsis
maxWidthstring | number136Maximum width for truncated tags (in pixels if number, or CSS units if string)
interactivebooleantrueWhether the tags are interactive (show hover effects and cursor pointer)
colorrefer here'gray'All HLTag color option available
borderedboolean-Whether the tags have a border
tooltipVariant'light' | 'dark''dark'The variant of the tooltip shown on the tag counter
customColorsHLTagColor[]-Per-tag colors applied by index (cycled across tags), overriding color

Emits

NameParametersDescription
@update:value(value: string[]) => voidFired when the tag list changes (add or remove)
@input(value: string) => voidFired as the user types in the input, with the current input text
@close({ event: Event, index: number })Fired when a tag's close button is clicked
@click({ event: Event, tag: any, index: number })Fired when a tag is clicked
@update:counter(count: number) => voidFired when the collapsed-overflow counter changes (with max as a number)
@update:overflow(overflow: boolean) => voidFired when tags start or stop overflowing the visible limit

Slots

NameParametersDescription
popover-content()Content of the popover shown while adding a tag (e.g. a filter list)
customRender{ tag, index }Custom rendering for each tag's label
customRenderAvatar{ tag, index }Custom rendering for each tag's avatar

Methods

Access these via a template ref on the component.

MethodParametersDescription
setInputValue(value: any)Programmatically add a tag (e.g. from a popover option)