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
- Oklab and OkLCh — comes from the design tokens
- Masks — covered by a -webkit- fallback, not counted in the minimum
- Logical properties
- :where()
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
AvatarGroupPropsAvatarGroupStackItemPropsAvatarGroupSizeAvatarGroupVariant
Minimal Composition
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 contextAvatarStackItem: one stacked avatar item
Optional Parts
- custom
AvatarImageandAvatarFallbackcontent inside each item
Composition Order
AvatarStack- repeated
AvatarStackItem - avatar content inside each item
Variants
- Size: shared avatar size from
2xsthrough2xl - Variant:
primaryorsecondary - Content: image avatars, fallback avatars, or mixed groups
Edge Cases
- Very large groups usually need truncation or a
+Nsummary 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.
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.
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.
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'.
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.
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
AvatarStack- repeated
AvatarStackItem - avatar image/fallback content
Valid Composition Patterns
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.