Avatar Stack
A row of overlapping circular avatars; on hover or focus the avatar lifts and scales up, its neighbours move aside, and a name tooltip floats out.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add avatar-stack.enAvatar Stack is a React avatar group component built with Motion (motion/react) and Tailwind CSS. Avatars overlap left over right; hovering or keyboard-focusing one lifts and enlarges it while its neighbours move aside, and its name appears above. People beyond max collapse into a +N bubble, and avatars without an image get a stable gradient and initial derived from the name.
Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<AvatarStack/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
When to use
- Showing who is currently viewing a document in a collaboration tool
- Listing assignees on a project card or task row
- An event or course page showing "N people registered" with a few faces
- Social proof in a homepage hero next to copy such as "used by 10,000+ teams"
- Not a good fit: when you need to show full names, titles and photos for each person, use Team Grid Section
Installation
npx webberui@latest add https://webberui.com/r/avatar-stack.en.jsonOr, once registries are configured in components.json, install it as @webberui/avatar-stack.
Usage
import { AvatarStack } from "@/components/ui/avatar-stack";
<AvatarStack
avatars={[
{ src: "/team/ada.jpg", alt: "Ada Lovelace" },
{ alt: "Alan Turing" },
{ alt: "Grace Hopper", fallback: "GH" },
]}
max={5}
size={40}
/>;Examples
Text avatars from names
Without src, the first character of alt becomes the avatar text. The background is computed from the name, so the same person always gets the same color.
import { AvatarStack } from "@/components/ui/avatar-stack";
<AvatarStack
avatars={[
{ alt: "Maya Chen" },
{ alt: "Liam Ortiz" },
{ alt: "Sofia Rossi" },
{ alt: "Noah Kim" },
]}
size={36}
/>;Collaborators on a document card
A smaller size and a low max fit a card corner; the rest show up as +N.
<div className="flex items-center justify-between rounded-xl border p-4">
<div>
<p className="text-sm font-medium">2026 Q4 product plan</p>
<p className="text-xs text-neutral-500">Updated 10 minutes ago</p>
</div>
<AvatarStack avatars={editors} max={3} size={28} />
</div>Map API data
Convert user records from your backend into AvatarStackItem; the type can be imported directly.
import { AvatarStack, type AvatarStackItem } from "@/components/ui/avatar-stack";
type Member = { id: string; name: string; avatarUrl: string | null };
function MemberAvatars({ members }: { members: Member[] }) {
const avatars: AvatarStackItem[] = members.map((m) => ({
src: m.avatarUrl ?? undefined,
alt: m.name,
}));
return <AvatarStack avatars={avatars} max={4} />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
avatars | AvatarStackItem[] | — | The avatars, stacked left to right in order (left on top of right) |
max | number | 5 | How many avatars to show at most; the rest are collapsed into a +N bubble |
size | number | 40 | Avatar diameter (px) |
className | string | — | Custom styles for the outer container |
AvatarStackItem
| Field | Type | Description |
|---|---|---|
src | string? | Avatar image URL; when omitted, the text fallback is shown |
alt | string | Alternative text, also used as the name shown in the tooltip |
fallback | string? | Character shown when there is no image; when omitted, the first character of alt is used |
How it works
- Avatars overlap via negative margins, with the left one on top of the right one (
z-indexdecreases from left to right). - On hover or keyboard focus of any avatar, that avatar lifts and scales up and takes the topmost layer, its left and right neighbours move aside, and a name tooltip floats out above it.
- With no image, a stable gradient background is generated from the name and the first character is shown; the same name always produces the same result.
- Avatars beyond
maxare collapsed into a+Nbubble that follows the visible avatars.
Accessibility
- Each avatar is a
buttonwith anaria-label(the name), can be focused with Tab, and shows the tooltip on focus just as it does on hover. - The tooltip is purely visual (
aria-hidden); the name is already exposed to assistive technology througharia-label, which avoids it being announced twice. - The
+Nbubble carriesrole="img"and anaria-label(such as “remaining 2 member”, "2 more members"). - When the user has "reduce motion" enabled at the system level, the lift, the scale-up, and the offsetting are disabled; the tooltip still appears.
FAQ
Does it work with the Next.js App Router?
Yes. The file already starts with "use client", so import it from a Server Component page as usual and pass server-fetched data in as props.
I passed two letters as fallback; why is only one shown?
Text avatars show a single character: the first character of fallback (or alt if there is none), uppercased. If you need a two-letter monogram, provide an image instead.
Does it use next/image?
No, it renders a plain <img> with object-cover inside the circle. The image comes from your URL, so point it at an already resized version if you want smaller files.
How does it work with keyboards and screen readers?
Each avatar is a button with an aria-label, reachable with Tab; focusing one shows the name just like hover. The +N bubble announces how many more members there are. With the OS "reduce motion" setting on, the lift and shift are disabled.
What are the dependencies?
motion, clsx and tailwind-merge. You can also copy the source from the Manual tab in the installation section.
Animated Stats
A group of statistics that count from 0 to their target values when they enter the viewport, with items staggering in and sliding up.
Testimonial Wall
A three-column testimonial wall whose columns scroll in alternating directions, in either an auto infinite-scroll or a scroll-parallax style.