Avatar Group
Group multiple avatars together with various display options
Basic Usage
Pass an options array; each entry renders as an avatar, in order.
<template>
<HLAvatarGroup id="basic-avatar-group" :options="options" size="md" />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
// License-free avatars via DiceBear (https://dicebear.com)
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>Stacked Avatars
Add stacked to overlap the avatars instead of spacing them apart, taking up less horizontal room.
<template>
<HLAvatarGroup id="stacked-avatar-group" :options="options" size="md" stacked />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>With Tooltips
Enable tooltip to show each avatar's name on hover — useful when avatars are stacked. Set tooltipTheme to dark (default) or light.
Dark Theme
Light Theme
<template>
<!-- Dark tooltip (default) -->
<HLAvatarGroup id="tooltip-avatar-group" :options="options" size="md" stacked tooltip />
<!-- Light tooltip -->
<HLAvatarGroup id="tooltip-avatar-group-light" :options="options" size="md" stacked tooltip tooltipTheme="light" />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>Tooltip Placement
Use placement to choose which side of the avatar its name tooltip opens on. Accepts the four sides (top, bottom, left, right) and their -start / -end alignment variants — 12 values in all. Pick the placement that keeps the tooltip inside the viewport for where the group sits in your layout.
INFO
placement applies to the per-avatar name tooltip only. The +N overflow popover has its own fixed placement and is unaffected.
<template>
<HLSelect
id="avatar-group-placement-select"
:options="placementOptions"
:value="placement"
@update:value="value => (placement = value)"
/>
<HLAvatarGroup id="placement-avatar-group" :options="options" size="md" stacked tooltip :placement="placement" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLAvatarGroup, HLSelect } from '@platform-ui/highrise'
import type { HLPopoverPlacement } from '@platform-ui/highrise'
const placement = ref<HLPopoverPlacement>('top')
const placementOptions = [
'top',
'top-start',
'top-end',
'bottom',
'bottom-start',
'bottom-end',
'left',
'left-start',
'left-end',
'right',
'right-start',
'right-end',
].map(value => ({ label: value, value }))
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', border: true },
]
</script>With Max Display
Set max to cap the number of visible avatars; the rest collapse into a +N avatar that lists the remaining names on hover. Use overflowTheme to theme that overflow tooltip. When max is greater than or equal to the number of options, every avatar is shown and no +N avatar appears — a common way to render the whole list is to set max to a large number.
Light Theme
Dark Theme
<template>
<HLAvatarGroup id="max-avatar-group" :options="options" size="md" stacked tooltip :max="2" overflowTheme="light" tooltipTheme="light" />
<HLAvatarGroup id="max-avatar-group-dark" :options="options" size="md" stacked tooltip :max="2" overflowTheme="dark" tooltipTheme="dark" />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>With Action Button
Set action to append a trailing button after the avatars. Fill the #action slot with your icon; the button emits @on-action when clicked.
<template>
<div>
<HLAvatarGroup id="action-avatar-group" :options="options" size="md" stacked tooltip :max="2" action @on-action="onAction">
<template #action>
<PlusIcon />
</template>
</HLAvatarGroup>
<div class="mt-4 text-sm space-y-2">
<div v-for="(event, index) in events" :key="index" class="text-gray-500">{{ event.timestamp }} - {{ event.name }}</div>
<div v-if="!events.length" class="text-gray-400">No events yet</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLAvatarGroup } from '@platform-ui/highrise'
import { PlusIcon } from '@gohighlevel/ghl-icons/24/outline'
const events = ref([])
const onAction = () => {
events.value.push({ name: '@on-action', timestamp: new Date().toLocaleTimeString() })
}
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>Removable Avatars
Set removable to render a small remove (✕) button on each avatar. Clicking it emits @remove with that option's value, so you can drop the entry from your own list.
INFO
Each option must have a unique value for it to be removable — @remove only fires for options whose value is defined, and the value is what's passed back to your handler.
<template>
<HLAvatarGroup id="removable-avatar-group" :options="members" size="md" stacked tooltip removable @remove="handleRemove" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLAvatarGroup } from '@platform-ui/highrise'
// Each option needs a unique `value` so it can be identified on remove
const members = ref([
{ value: 'olivia', name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', border: true },
{ value: 'ethan', name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', border: true },
{ value: 'mia', name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', border: true },
])
const handleRemove = ({ value }) => {
members.value = members.value.filter(member => member.value !== value)
}
</script>Initials Background Colors
When an avatar has no image it falls back to initials. Set preferredInitialsBgColor for the initials background at two levels:
- group-level to color the
+Noverflow avatar - per-option to color an individual initials avatar
AB
SG
TG
<template>
<HLAvatarGroup
id="initials-colors-avatar-group"
:options="initialsColorOptions"
size="md"
stacked
tooltip
:max="3"
preferredInitialsBgColor="var(--gray-100)"
/>
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
const initialsColorOptions = [
{ name: 'Alex Blue', preferredInitialsBgColor: 'var(--primary-50)' },
{ name: 'Sam Green', preferredInitialsBgColor: 'var(--success-100)' },
{ name: 'Taylor Gray', preferredInitialsBgColor: 'var(--gray-200)' },
{ name: 'Jordan Orange', preferredInitialsBgColor: 'var(--warning-100)' },
{ name: 'Chris' },
]
</script>Sizes
Use size (3xs–md) to scale every avatar in the group.
<template>
<HLSpace vertical>
<HLAvatarGroup id="3xs-avatar-group" :options="options" size="3xs" stacked tooltip />
<HLAvatarGroup id="2xs-avatar-group" :options="options" size="2xs" stacked tooltip />
<HLAvatarGroup id="xs-avatar-group" :options="options" size="xs" stacked tooltip />
<HLAvatarGroup id="sm-avatar-group" :options="options" size="sm" stacked tooltip />
<HLAvatarGroup id="md-avatar-group" :options="options" size="md" stacked tooltip />
</HLSpace>
</template>
<script setup lang="ts">
import { HLAvatarGroup, HLSpace } from '@platform-ui/highrise'
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>With Scroll
When the +N overflow tooltip lists more names than its height can show, the list scrolls so you can reach all of them.
<template>
<HLAvatarGroup id="scroll-avatar-group" :options="optionsForScroll" size="md" stacked tooltip />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
// A long list so the overflow popover scrolls
const optionsForScroll = Array.from({ length: 14 }, (_, i) => ({
name: `Member ${i + 1}`,
src: `https://api.dicebear.com/9.x/avataaars/svg?seed=Member${i + 1}`,
}))
</script>With Status Indicator
Set statusIndicator to show a small dot on each avatar. Give each option a semantic color (e.g. success, warning, error) to set its dot color.
INFO
The status indicator only renders when stacked is false — a stacked group hides the indicators to avoid them overlapping. It also requires a size of md, sm, or xs; on smaller avatars the indicator is omitted because it would be too small to see.
<template>
<HLAvatarGroup id="status-avatar-group" :options="options" size="md" :stacked="false" tooltip :status-indicator="true" />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
const options = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', color: 'success', border: true }, // color = indicator color
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', color: 'warning', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', color: 'error', border: true },
]
</script>With Custom Status Indicator
Set value (a number or short string) with dot: false to show that content on the indicator instead of a plain dot.
<template>
<HLAvatarGroup id="custom-status-avatar-group" :options="customBadgeOptions" size="md" :stacked="false" tooltip :status-indicator="true" />
</template>
<script setup lang="ts">
import { HLAvatarGroup } from '@platform-ui/highrise'
// `value` sets the indicator content (number or string); `dot: false` shows the value instead of a plain dot
const customBadgeOptions = [
{ name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', value: 7, dot: false, color: 'error', border: true },
{ name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', value: '!', dot: false, color: 'success', border: true },
{ name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', value: '+', dot: false, color: 'warning', border: true },
]
</script>Event Testing
This example wires up both events the group emits. Click the trailing + button to fire @on-action, or hover an avatar and click its ✕ to fire @remove with that option's value. Every event is appended to the log below.
Event Log:
<template>
<HLAvatarGroup
id="events-avatar-group"
:options="members"
size="md"
stacked
tooltip
removable
action
:max="3"
@remove="handleRemove"
@on-action="handleAction"
>
<template #action>
<PlusIcon />
</template>
</HLAvatarGroup>
<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.</div>
<div v-for="(log, index) in eventLog" :key="index" class="text-gray-700">
{{ log.timestamp }}: {{ log.name }}
<div v-if="log.details" class="ml-4 text-gray-500 text-xs">{{ log.details }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLAvatarGroup } from '@platform-ui/highrise'
import { PlusIcon } from '@gohighlevel/ghl-icons/24/outline'
const eventLog = ref([])
const logEvent = (name: string, details?: string) => {
eventLog.value.unshift({ name, details, timestamp: new Date().toLocaleTimeString() })
if (eventLog.value.length > 5) eventLog.value.pop()
}
// Each option needs a unique `value` so @remove can identify it
const members = ref([
{ value: 'olivia', name: 'Olivia Hart', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Olivia', border: true },
{ value: 'ethan', name: 'Ethan Cole', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Ethan', border: true },
{ value: 'mia', name: 'Mia Reyes', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Mia', border: true },
{ value: 'noah', name: 'Noah Patel', src: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Noah', border: true },
])
const handleRemove = ({ value }) => {
members.value = members.value.filter(member => member.value !== value)
logEvent('@remove', `Removed value: ${value}`)
}
const handleAction = () => logEvent('@on-action', 'Action button clicked')
</script>Accessibility
- When an
srcis provided for the avatar, make sure to passalt-textinimgProps.altfor complete a11y support - In cases where
srcandnameprops are provided butimgProps.altis not, thenamewill be used as the fallback forimgProps.alt - A11y checks will fail if neither
namenorimgProps.altis provided
Imports
import { HLAvatarGroup } from '@platform-ui/highrise'
import type { HLAvatarGroupSize } from '@platform-ui/highrise'Props
| Prop | Type | Default | Description |
|---|---|---|---|
| id * | string | undefined | undefined | Unique identifier for the avatar group |
| size | 'md' | 'sm' | 'xs' | '2xs' | '3xs' | 'md' | Size of the avatars in the group |
| stacked | boolean | false | Whether avatars should overlap each other |
| tooltip | boolean | false | Show tooltips with avatar names on hover |
| action | boolean | false | Show an action button at the end of the group |
| options | Array<AvatarOption> | [] | Array of avatar options to display |
| max | number | 3 | Maximum number of avatars to display |
| tooltipTheme | 'light' | 'dark' | 'dark' | Theme of the individual avatar tooltip |
| overflowTheme | 'light' | 'dark' | 'light' | Theme of the avatar group overflow tooltip |
| preferredInitialsBgColor | string | undefined | Background color for the +N overflow avatar initials |
| statusIndicator | boolean | false | Show a status (dot) indicator on each avatar. Only renders when stacked is false and size is md, sm, or xs |
| removable | boolean | false | Show a remove (✕) button on each avatar. Emits @remove with the option's value on click (requires each option to have a value) |
| placement | HLPopoverPlacement — 'top' | 'bottom' | 'left' | 'right' plus their -start / -end variants | 'top' | Where each avatar's name tooltip opens relative to the avatar. Does not affect the +N overflow popover, which always opens below |
Types
AvatarOption
| Property | Type | Description |
|---|---|---|
| name * | string | Tooltip label and initials source. Required even when src is set — an option without a name is skipped entirely (the group only renders options that have a name, unlike a standalone HLAvatar). |
| src | string | Source of the avatar image. If not provided, the component picks up initials from the name. |
| imgProps | ImgHTMLAttributes | Props to attach to the <img /> element when src is passed to avatar |
| preferredInitialsBgColor | string | Custom initials background color for this avatar option |
Status-indicator properties
Note that these only take effect when the group has statusIndicator and stacked is false
| Property | Type | Description |
|---|---|---|
| color | HLBadgeColor | Color of the dot / indicator (e.g. success, warning, error) |
| dot | boolean | Show a plain dot indicator. Set dot: false with a value to show the value instead |
| value | number | string | (() => VNodeChild) | VNode | Component | Indicator content — a number/string, or a component/render function for a custom indicator |
| border | boolean | Whether to show a border around the indicator |
| offset | [number | string, number | string] | Offset of the indicator from its default position |
| processing | boolean | Whether the indicator shows a processing animation |
| showZero | boolean | Whether to show the indicator when value is 0 |
| show | boolean | Whether to show the indicator |
Emits
| Name | Parameters | Description |
|---|---|---|
@on-action | (): void | Triggered when action button clicked |
@remove | (option: { value?: string | number }): void | Triggered when an avatar's remove button is clicked (when removable); receives the removed option's value |
Slots
| Name | Parameters | Description |
|---|---|---|
| action | () | Custom icon for action button |