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

Avatar Group

Group multiple avatars together with various display options

Basic Usage

Pass an options array; each entry renders as an avatar, in order.

Vue
vue
<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.

Vue
vue
<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
Vue
vue
<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.

Vue
vue
<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
+1
Dark Theme
+1
Vue
vue
<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.

+1
No events yet
Vue
vue
<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.

Vue
vue
<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 +N overflow avatar
  • per-option to color an individual initials avatar

AB

SG

TG

+2
Vue
vue
<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 (3xsmd) to scale every avatar in the group.

Vue
vue
<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.

+11
Vue
vue
<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.

Vue
vue
<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.

777
!
+
Vue
vue
<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.

+1

Event Log:

No events logged yet. Click the + button, or hover an avatar and click its ✕.
Vue
vue
<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

  1. When an src is provided for the avatar, make sure to pass alt-text in imgProps.alt for complete a11y support
  2. In cases where src and name props are provided but imgProps.alt is not, the name will be used as the fallback for imgProps.alt
  3. A11y checks will fail if neither name nor imgProps.alt is provided

Imports

ts
import { HLAvatarGroup } from '@platform-ui/highrise'
import type { HLAvatarGroupSize } from '@platform-ui/highrise'

Props

PropTypeDefaultDescription
id *string | undefinedundefinedUnique identifier for the avatar group
size'md' | 'sm' | 'xs' | '2xs' | '3xs''md'Size of the avatars in the group
stackedbooleanfalseWhether avatars should overlap each other
tooltipbooleanfalseShow tooltips with avatar names on hover
actionbooleanfalseShow an action button at the end of the group
optionsArray<AvatarOption>[]Array of avatar options to display
maxnumber3Maximum 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
preferredInitialsBgColorstringundefinedBackground color for the +N overflow avatar initials
statusIndicatorbooleanfalseShow a status (dot) indicator on each avatar. Only renders when stacked is false and size is md, sm, or xs
removablebooleanfalseShow a remove (✕) button on each avatar. Emits @remove with the option's value on click (requires each option to have a value)
placementHLPopoverPlacement'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

PropertyTypeDescription
name *stringTooltip 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).
srcstringSource of the avatar image. If not provided, the component picks up initials from the name.
imgPropsImgHTMLAttributesProps to attach to the <img /> element when src is passed to avatar
preferredInitialsBgColorstringCustom 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

PropertyTypeDescription
colorHLBadgeColorColor of the dot / indicator (e.g. success, warning, error)
dotbooleanShow a plain dot indicator. Set dot: false with a value to show the value instead
valuenumber | string | (() => VNodeChild) | VNode | ComponentIndicator content — a number/string, or a component/render function for a custom indicator
borderbooleanWhether to show a border around the indicator
offset[number | string, number | string]Offset of the indicator from its default position
processingbooleanWhether the indicator shows a processing animation
showZerobooleanWhether to show the indicator when value is 0
showbooleanWhether to show the indicator

Emits

NameParametersDescription
@on-action(): voidTriggered when action button clicked
@remove(option: { value?: string | number }): voidTriggered when an avatar's remove button is clicked (when removable); receives the removed option's value

Slots

NameParametersDescription
action()Custom icon for action button