An accessible compact row of people with initials, image support, and an overflow count.
Design review
5 collaborators
import { AvatarStack } from "@/components/ballmac/avatar-stack"
export default function AvatarStackDemo() {
return (
<div className="flex w-full max-w-xs items-center justify-between gap-4 rounded-xl border border-border bg-card p-4">
<div>
<p className="text-sm font-medium">Design review</p>
<p className="text-muted-foreground mt-1 text-xs">5 collaborators</p>
</div>
<AvatarStack
people={[
{ name: "Alex Morgan" },
{ name: "Sam Lee" },
{ name: "Taylor Chen" },
{ name: "Jordan Patel" },
{ name: "Morgan Reed" },
]}
max={3}
/>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/avatar-stackCopy the source into your project.
components/ballmac/avatar-stack.tsx// Ballmac UI: Avatar Stack. https://ui.ballmac.com/components/avatar-stack import * as React from "react" import { cn } from "@/lib/utils" type AvatarPerson = { /** Display name for screen readers and tooltip. */ name: string /** Optional image URL. */ src?: string /** Initials when no image is present. */ initials?: string } type AvatarStackProps = React.ComponentProps<"div"> & { /** People to display, in order. */ people: AvatarPerson[] /** Maximum visible avatars before the overflow count. */ max?: number /** Avatar diameter. */ size?: "sm" | "default" } function AvatarStack({ className, people, max = 4, size = "default", ...props }: AvatarStackProps) { const visible = people.slice(0, Math.max(0, max)) const overflow = people.length - visible.length return ( <div data-slot="avatar-stack" data-size={size} role="group" aria-label={people.map((person) => person.name).join(", ")} className={cn( "group/avatar-stack flex min-w-0 items-center -space-x-2", className, )} {...props} > {visible.map((person, index) => ( <span key={`${person.name}-${index}`} data-slot="avatar-stack-person" title={person.name} aria-hidden="true" className="bg-muted text-muted-foreground border-background relative flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full border-2 text-xs font-semibold data-[size=sm]:size-7 group-data-[size=sm]/avatar-stack:size-7" > <span> {person.initials ?? person.name .split(/\s+/) .map((part) => part[0]) .slice(0, 2) .join("")} </span> {person.src && ( <img src={person.src} alt="" className="absolute inset-0 size-full object-cover" /> )} </span> ))} {overflow > 0 && ( <span data-slot="avatar-stack-overflow" aria-hidden="true" className="bg-secondary text-secondary-foreground border-background relative flex size-9 shrink-0 items-center justify-center rounded-full border-2 text-xs font-semibold group-data-[size=sm]/avatar-stack:size-7" > +{overflow} </span> )} </div> ) } export { AvatarStack, type AvatarStackProps, type AvatarPerson }Update the import paths to match your project setup.
Usage
import { AvatarStack } from "@/components/ballmac/avatar-stack"The full example is in the Code tab above.
Examples
States and variants
import { AvatarStack } from "@/components/ballmac/avatar-stack"
export default function AvatarStackStates() {
return (
<div className="flex w-full max-w-xs flex-col gap-4">
<AvatarStack
size="sm"
people={[{ name: "Alex Morgan" }, { name: "Sam Lee" }]}
/>
<AvatarStack
people={[
{ name: "Alex Morgan" },
{ name: "Sam Lee" },
{ name: "Taylor Chen" },
]}
max={2}
/>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
people*People to display, in order. | AvatarPerson[] | — |
maxMaximum visible avatars before the overflow count. | number | 4 |
sizeAvatar diameter. | "sm" | "default" | "default" |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| None | Static content is announced with semantic structure |
Use with AI
An accessible compact row of people with initials, image support, and an overflow count. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Avatar Stack (@ballmac/avatar-stack) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Show participants or collaborators in limited space
Not for
- Use Avatar for one person
Registry JSON: https://ui.ballmac.com/r/avatar-stack.json
Credits
Free to use in personal and commercial projects.
- npm
- None
- Registry
- shadcn/utils
Pairs well with
Avatar
A Radix avatar with image and initials fallback, three sizes, an optional presence dot, and AvatarGroup for overlapping stacks with a +N overflow counter.
Activity Feed
A compact actor and action feed with timestamps and an empty state.
Audio Player
A podcast-style audio player: play and pause, skip back and forward, a scrubber with chapter ticks, a named current chapter and a speed control. Works with a real file or as a silent demo.
Avatar Circles
Overlapping avatars that spread on hover, lift individually with a name and role tooltip, show presence, fall back to initials and end in a +N count that can be a button.