Avatar Group
zyeon/avatar-groupFreeComponent
An overlapping avatar stack with ring cutouts, letter fallbacks for broken images, and a computed +N overflow chip.
Install it
npx shadcn@latest add https://ui.zyeon.ai/r/avatar-group.jsonSource
1"use client"23import * as React from "react"4import { cn } from "@/lib/utils"56const SIZES = {7 sm: { avatar: "size-6 text-[10px]", overlap: "-space-x-1.5" },8 md: { avatar: "size-8 text-xs", overlap: "-space-x-2" },9 lg: { avatar: "size-10 text-sm", overlap: "-space-x-2.5" },10} as const1112/** Shared shell for a disc: ring-background matches the page, so the overlap reads as a cut-out */13const CIRCLE =14 "relative flex shrink-0 select-none items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-2 ring-background"1516/** Hovering one disc: raising z reveals it and stays, the lift is decoration (off under reduced-motion) */17const LIFT =18 "transition-transform duration-200 ease-out hover:z-10 hover:-translate-y-1 motion-reduce:transition-none motion-reduce:hover:translate-y-0"1920export interface AvatarGroupItem {21 src?: string22 alt: string23 /** Fallback letter; defaults to the first character of `alt`, uppercased. */24 fallback?: string25}2627export interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {28 items: AvatarGroupItem[]29 /** Anything beyond this collapses into a "+N" disc, N computed live. */30 max?: number31 size?: keyof typeof SIZES32}3334function AvatarItem({ item, sizeClass }: { item: AvatarGroupItem; sizeClass: string }) {35 const [failed, setFailed] = React.useState(false)36 const showImage = Boolean(item.src) && !failed3738 return (39 <span className={cn(CIRCLE, LIFT, sizeClass)}>40 {showImage ? (41 // eslint-disable-next-line @next/next/no-img-element -- registry components stay framework-agnostic, no next/image42 <img43 alt={item.alt}44 className="size-full object-cover"45 onError={() => setFailed(true)}46 src={item.src}47 />48 ) : (49 <span aria-label={item.alt} role="img">50 {item.fallback ?? item.alt.charAt(0).toUpperCase()}51 </span>52 )}53 </span>54 )55}5657export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(58 ({ items, max = 5, size = "md", className, ...props }, ref) => {59 const { avatar, overlap } = SIZES[size]60 const visible = items.length > max ? items.slice(0, max) : items61 const hidden = items.length - visible.length6263 return (64 <div className={cn("flex items-center", overlap, className)} ref={ref} {...props}>65 {visible.map((item, i) => (66 <AvatarItem item={item} key={`${item.alt}-${i}`} sizeClass={avatar} />67 ))}68// … truncated
What it pulls in
Other registry items
Files it writes
More from zyeon
All 893 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| A/B Test Resultab-test-result | zyeon | Components | Paid | 2 deps · 2 files | |
| Accordionaccordion | zyeon | Components | Free | 1 dep | |
| Activity Feedactivity-feed | zyeon | Components | Free | 2 deps · 2 files | |
| Activity Heatmapactivity-heatmap | zyeon | Components | Paid | 1 dep · 2 files | |
| Address Formaddress-form | zyeon | Components | Paid | 1 dep | |
| Agent Graphagent-graph | zyeon | Components | Free | 2 deps · 2 files | |
| Agent Handoffagent-handoff | zyeon | Components | Free | 1 dep | |
| Agent Inboxagent-inbox | zyeon | Components | Free | 2 deps · 2 files |
