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.
<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.
<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.
<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.
<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.
<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
:max="3" — always three tags
:max="1" — a single tag plus the counter
<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.
<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.
<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.
<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.
<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.
<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.
<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.
<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.
<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.
Event Log:
<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
import { HLTagGroup } from '@platform-ui/highrise'Accessibility
- Describe the collection with
aria-label/aria-labelledby. - Update each tag’s
aria-pressed/aria-selectedso assistive tech hears which values are active. - Link helper text (such as max selections) via
aria-describedbyon the group.
Props
| Name | Type | Default | Description |
|---|---|---|---|
| id | string | - | The id of the dynamic tags |
| defaultValue | array | - | The default value of the dynamic tags |
| max | number | 'responsive' | responsive | The 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 |
| disabled | boolean | false | Whether the dynamic tags is disabled |
| closable | boolean | true | Whether the dynamic tags is closable |
| round | boolean | true | Whether the dynamic tags is round |
| canAddTag | boolean | true | Whether the dynamic tags can add tag |
| delimiter | Array<'space' | 'comma' | 'enter'> | ['enter'] | Whether the dynamic tags can add tag on comma or space or enter |
| truncate | boolean | false | Whether to truncate tag text with ellipsis |
| maxWidth | string | number | 136 | Maximum width for truncated tags (in pixels if number, or CSS units if string) |
| interactive | boolean | true | Whether the tags are interactive (show hover effects and cursor pointer) |
| color | refer here | 'gray' | All HLTag color option available |
| bordered | boolean | - | Whether the tags have a border |
| tooltipVariant | 'light' | 'dark' | 'dark' | The variant of the tooltip shown on the tag counter |
| customColors | HLTagColor[] | - | Per-tag colors applied by index (cycled across tags), overriding color |
Emits
| Name | Parameters | Description |
|---|---|---|
@update:value | (value: string[]) => void | Fired when the tag list changes (add or remove) |
@input | (value: string) => void | Fired 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) => void | Fired when the collapsed-overflow counter changes (with max as a number) |
@update:overflow | (overflow: boolean) => void | Fired when tags start or stop overflowing the visible limit |
Slots
| Name | Parameters | Description |
|---|---|---|
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.
| Method | Parameters | Description |
|---|---|---|
setInputValue | (value: any) | Programmatically add a tag (e.g. from a popover option) |