AvatarGroup Displays a stack of avatars with overflow handling.
npx vayu-ui add avatargroup
npx vayu-ui add -t avatargroup
Preview Code
Grid Layout (All Visible) Interactive (Click avatars to select) const users = [
{ id: 1 , username: "John Doe" , src: "https://github.com/shadcn.png" , status: "online" as const },
{ id: 2 , username: "Jane Smith" , fallback: "JS" , status: "offline" as const },
{ id: 3 , username: "Bob Wilson" , src: "https://github.com/vercel.png" , status: "online" as const },
{ id: 4 , username: "Alice Johnson" , fallback: "AJ" , status: "away" as const },
{ id: 5 , username: "Charlie Brown" , src: "https://github.com/octocat.png" , status: "busy" as const },
{ id: 6 , username: "David Lee" , fallback: "DL" , status: "offline" as const },
];
< AvatarGroup users = {users} maxDisplay = { 4 } />
< AvatarGroup users = {users} layout = "grid" maxDisplay = { 6 } />
< AvatarGroup users = {users. slice ( 0 , 3 )} size = "small" spacing = "tight" />
< AvatarGroup users = {users. slice ( 0 , 3 )} size = "medium" />
< AvatarGroup users = {users. slice ( 0 , 3 )} size = "large" spacing = "loose" />
< AvatarGroup users = {users. slice ( 0 , 3 )} size = "xlarge" />
< AvatarGroup
users = {users}
maxDisplay = { 5 }
onAvatarClick = {( user , index ) => console. log (user)}
onOverflowClick = {( hiddenUsers ) => console. log (hiddenUsers)}
/>
< AvatarGroup >
{ /* Root container — role="group" */ }
< AvatarItem >
{ /* Per-user avatar wrapper with Avatar inside */ }
< Avatar >
< Avatar.Image />
< Avatar.Status />
</ Avatar >
</ AvatarItem >
< AvatarItem >...</ AvatarItem >
< OverflowButton >{ /* "+N" indicator shown when users exceed maxDisplay */ }</ OverflowButton >
</ AvatarGroup >
Part Description Root Flex container with role="group" and keyboard handling AvatarItem Per-user wrapper — renders Avatar with positioning OverflowButton Button showing remaining count when overflow occurs
Key Behavior ArrowRightMove focus to the next interactive avatar ArrowLeftMove focus to the previous interactive avatar TabMove focus into and out of the avatar group EnterActivate the focused clickable avatar SpaceActivate the focused clickable avatar
Element Attribute Value Root rolegroupRoot aria-labelAvatar group with {count} membersAvatar (static) aria-label (from Avatar){username}'s avatarAvatar (clickable) rolebuttonAvatar (clickable) tabIndex0OverflowButton rolebuttonOverflowButton aria-labelShow {count} more users
Focus is managed via roving tabindex on interactive avatars (role="button")
focus-visible ring uses ring-focus design token with offset
All hover animations respect prefers-reduced-motion via motion-safe: prefix
Minimum touch target of 24x24px (WCAG 2.5.5)
A screen reader announces the group as "Avatar group with N members, list" followed by each avatar individually. Each avatar is announced using the username property (e.g., "John Doe's avatar, image"). Avatars without an image source fall back to the initials text. When the overflow button is present, it is announced as "Show N more users, button".
AvatarGroup/
├── AvatarGroup.tsx # Composition — data processing + layout
├── AvatarItem.tsx # Individual avatar wrapper with positioning
├── AvtarGroupOverflowButton.tsx # "+N more" overflow indicator
├── hooks.ts # Spacing calculation and keyboard navigation
├── types.ts # Type definitions
├── index.ts # Public API exports
└── README.md
Prop Type Default Description usersUserData[][]Array of user objects to display size"small" | "medium" | "large" | "xlarge""medium"Size of each avatar maxDisplaynumber5Maximum visible avatars before overflow layout"stack" | "grid""stack"Layout mode for the group spacing"tight" | "normal" | "loose" | number"normal"Overlap offset for stack layout renderOverflow(count: number) => React.ReactNode— Custom render for the overflow indicator onAvatarClick(user: UserData, index: number) => void— Click handler for individual avatars onOverflowClick(hiddenUsers: UserData[]) => void— Click handler for the overflow button
Prop Type Description idstring | number | nullUnique identifier for the user srcstringImage URL for the avatar usernamestringDisplay name, used as alt text altstringAlt text for the avatar image fallbackstringInitials shown when no image status"online" | "offline" | "away" | "busy"Status indicator on the avatar
Size Dimensions small32x32px medium48x48px large64x64px xlarge96x96px
Spacing Stack Offset tight-12px normal-8px loose-4px