Skip to content
v2.5.0
English
Русский
Documentation

AvatarStack

AvatarStack is a compound component for overlapping avatar groups such as participants, reviewers, and team previews.
AL
BK
CM
avatar-stack.tsx · 5 lines
import { AvatarFallback, AvatarStack, AvatarStackItem } from '@vega-ui/react';

<AvatarStack>
  {['AL', 'BK', 'CM'].map((initials) => <AvatarStackItem key={initials}><AvatarFallback>{initials}</AvatarFallback></AvatarStackItem>)}
</AvatarStack>
Browser support
Widely available
  • Chrome 111
  • Edge 111
  • Firefox 113
  • Safari 15.4
  • Safari on iOS 15.4
Bundle size
Tested in
10 tests

Summary

AvatarStack is a compound component for overlapping avatar groups such as participants, reviewers, and team previews. The root shares size and variant with each stacked avatar item and turns several identities into one compact grouped display.

Imports

import {
  AvatarStack,
  AvatarStackItem,
  type AvatarGroupProps,
  type AvatarGroupSize,
  type AvatarGroupStackItemProps,
  type AvatarGroupVariant,
} from '@vega-ui/react';

Exported Types

  • AvatarGroupProps
  • AvatarGroupStackItemProps
  • AvatarGroupSize
  • AvatarGroupVariant

Minimal Composition

minimal-composition.tsx · 17 lines
import { AvatarFallback, AvatarImage, AvatarStack, AvatarStackItem } from '@vega-ui/react';

const portrait = (hue: number) => `data:image/svg+xml,${encodeURIComponent(`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><rect width='64' height='64' fill='hsl(${hue} 60% 82%)'/><circle cx='32' cy='25' r='11' fill='hsl(${hue} 35% 42%)'/><path d='M12 64c1-13 9-21 20-21s19 8 20 21z' fill='hsl(${hue} 35% 42%)'/></svg>`)}`;

export default function MinimalComposition() {
  return (
    <AvatarStack role='group' aria-label='Project members'>
      <AvatarStackItem role='img' aria-label='Maria Chen'>
        <AvatarImage src={portrait(210)} alt='' />
        <AvatarFallback>MC</AvatarFallback>
      </AvatarStackItem>
      <AvatarStackItem role='img' aria-label='Omar Haddad'>

Required Parts

  • AvatarStack: root layout and shared size/variant context
  • AvatarStackItem: one stacked avatar item

Optional Parts

  • custom AvatarImage and AvatarFallback content inside each item

Composition Order

  1. AvatarStack
  2. repeated AvatarStackItem
  3. avatar content inside each item

Variants

  • Size: shared avatar size from 2xs through 2xl
  • Variant: primary or secondary
  • Content: image avatars, fallback avatars, or mixed groups

Edge Cases

  • Very large groups usually need truncation or a +N summary pattern at the product level.
  • Mixing incompatible avatar sizes in one stack breaks the overlap effect.
  • Hover expansion changes spacing subtly and should be checked in dense layouts.
  • If explicit identity, roles, or ordering matter more than compactness, a text list or roster UI is usually clearer than a stack.

Common Mistakes

  • Rendering plain avatars without AvatarStackItem.
  • Mixing arbitrary non-avatar children into the stack.
  • Showing too many items without a summarizing pattern.

Examples

Basic

Basic: reviewers with an overflow count

The first three reviewers are shown and the rest collapse into a “+3” item computed from the list. The stack is a named role='group', each item is a named role='img', and the overflow item lists the hidden names in title.

reviewers.tsx · 38 lines
import { AvatarFallback, AvatarImage, AvatarStack, AvatarStackItem, Text } from '@vega-ui/react';

const portrait = (hue: number) => `data:image/svg+xml,${encodeURIComponent(`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><rect width='64' height='64' fill='hsl(${hue} 60% 82%)'/><circle cx='32' cy='25' r='11' fill='hsl(${hue} 35% 42%)'/><path d='M12 64c1-13 9-21 20-21s19 8 20 21z' fill='hsl(${hue} 35% 42%)'/></svg>`)}`;

const reviewers = [
  { name: 'Maria Chen', initials: 'MC', photo: portrait(210) },
  { name: 'Omar Haddad', initials: 'OH', photo: portrait(28) },
  { name: 'Lena Fischer', initials: 'LF', photo: '' },
  { name: 'Kenji Watanabe', initials: 'KW', photo: portrait(150) },
  { name: 'Ana Souza', initials: 'AS', photo: '' },
  { name: 'Tom Becker', initials: 'TB', photo: '' },
];

Controlled/Stateful

Controlled/Stateful: participants joining and leaving

The participant list lives in state; the stack and the summary next to it are derived from it. The summary is a role='status' region, so joins and leaves are announced, and the stack is removed entirely when nobody is left.

call-participants.tsx · 41 lines
import { useState } from 'react';
import { AvatarFallback, AvatarStack, AvatarStackItem, Button, Text } from '@vega-ui/react';

const people = [
  { name: 'Maria Chen', initials: 'MC' },
  { name: 'Omar Haddad', initials: 'OH' },
  { name: 'Lena Fischer', initials: 'LF' },
  { name: 'Kenji Watanabe', initials: 'KW' },
  { name: 'Ana Souza', initials: 'AS' },
];

export default function CallParticipants() {

Layout/Overlay

Layout/Overlay: participants in a dialog header

A compact size='xs' stack next to the dialog title is only a visual hint, so it is aria-hidden; the full attendee list below it carries the names. The title leaves room for the close button.

participants-dialog.tsx · 71 lines
import type { CSSProperties } from 'react';
import {
  Avatar,
  AvatarFallback,
  AvatarStack,
  AvatarStackItem,
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,

Error

Error: photos, failed images and anonymous members

Each item falls back on its own: a broken image shows the initials, a member without a photo shows initials directly, and an anonymous viewer gets an icon with variant='secondary'.

mixed-sources.tsx · 25 lines
import { AvatarFallback, AvatarIcon, AvatarImage, AvatarStack, AvatarStackItem } from '@vega-ui/react';
import { User } from '@vega-ui/icons';

const portrait = (hue: number) => `data:image/svg+xml,${encodeURIComponent(`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><rect width='64' height='64' fill='hsl(${hue} 60% 82%)'/><circle cx='32' cy='25' r='11' fill='hsl(${hue} 35% 42%)'/><path d='M12 64c1-13 9-21 20-21s19 8 20 21z' fill='hsl(${hue} 35% 42%)'/></svg>`)}`;

export default function MixedSources() {
  return (
    <AvatarStack role='group' aria-label='Document editors'>
      <AvatarStackItem role='img' aria-label='Maria Chen'>
        <AvatarImage src={portrait(210)} alt='' />
        <AvatarFallback>MC</AvatarFallback>
      </AvatarStackItem>

Edge

Edge: from one member to millions

Only four avatars are ever rendered; the remainder is formatted with Intl.NumberFormat compact notation (“+1.2K more”, “+2.4M more”). The group’s accessible name carries the exact total, so the shortened visual text is hidden from screen readers.

large-team.tsx · 36 lines
import { useState } from 'react';
import { AvatarFallback, AvatarStack, AvatarStackItem, Button, Text } from '@vega-ui/react';

const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const compact = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 });
const maxVisible = 4;

export default function LargeTeam() {
  const [total, setTotal] = useState(1248);

  const visibleCount = Math.min(total, maxVisible);
  const rest = total - visibleCount;

Anatomy

Overview

AvatarStack shares size and variant context with a sequence of AvatarStackItem children. Each item is still a real Avatar, but the stack-specific overlap behavior comes from the item class.

Required Parts

AvatarStack

Required. Owns layout direction and shared avatar context.

AvatarStackItem

Required in normal composition. Wraps each avatar and applies stacking/overlap behavior.

Optional Parts

AvatarImage / AvatarFallback

Optional avatar internals depending on whether an image is available.

Composition Order

  1. AvatarStack
  2. repeated AvatarStackItem
  3. avatar image/fallback content

Valid Composition Patterns

reviewers.tsx · 38 lines
import { AvatarFallback, AvatarImage, AvatarStack, AvatarStackItem, Text } from '@vega-ui/react';

const portrait = (hue: number) => `data:image/svg+xml,${encodeURIComponent(`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><rect width='64' height='64' fill='hsl(${hue} 60% 82%)'/><circle cx='32' cy='25' r='11' fill='hsl(${hue} 35% 42%)'/><path d='M12 64c1-13 9-21 20-21s19 8 20 21z' fill='hsl(${hue} 35% 42%)'/></svg>`)}`;

const reviewers = [
  { name: 'Maria Chen', initials: 'MC', photo: portrait(210) },
  { name: 'Omar Haddad', initials: 'OH', photo: portrait(28) },
  { name: 'Lena Fischer', initials: 'LF', photo: '' },
  { name: 'Kenji Watanabe', initials: 'KW', photo: portrait(150) },
  { name: 'Ana Souza', initials: 'AS', photo: '' },
  { name: 'Tom Becker', initials: 'TB', photo: '' },
];

Invalid Composition Patterns

Avatars rendered directly without AvatarStackItem

The group loses overlap and shared context behavior.

Mixed arbitrary child markup

The stack should remain a consistent avatar-only structure.

Inconsistent avatar sizes in one stack

Overlap masks and hover behavior become visually unstable.