Avatar
Photos with initials that keep their colour per person, presence dots, and stacks that name everyone.
Billing team
7 people
- online
Ada Okafor
Owner
- away
Lena Brandt
Admin
- busy
Tomás Reyes
Member
- offline
Mei Tanaka
Member
Installation
npx shadcn@latest add @quireui/avatarUsage
<Avatar name="Ada Okafor" src={user.photo} status="online" />name is all it needs. It gives the initials, the colour and the accessible name, so screen readers hear "Ada Okafor, online" rather than an image.
Colour per person
Without a photo, the initials sit on a soft tint picked from the name. The same name always gets the same hue, so people become recognisable by colour across the app, in lists, comments and assignees. The tint is mixed into the surface, so it works in light and dark. colored={false} gives a neutral one, for placeholders like "Unassigned".
Images
src shows a photo or a logo. While it loads the tint holds the space; the initials only appear if it takes longer than half a second or fails, so a fast load never flashes letters. shape="square" is for companies and teams, so they read differently from people at a glance.
Empty slots and badges
Leave out name for an empty slot, like an unassigned task: a dashed outline with a person icon, read as "Unassigned". badge marks something waiting for that person: a number shows a count (capped at 99+), true an unread dot. It sits in the top corner, apart from the status dot.
A new notification is a short, choreographed moment. The avatar dips as if something landed on it. The badge squeezes out of it like a drop of liquid, stretched as it leaves while two droplets fall back in behind it, then overshoots, wobbles and settles round. It lands with a small splash and one ripple, the number pops in, and a sheen passes over. When more arrive, the badge gulps and the number rolls, up for more and down for fewer. Cleared, it is drawn back in. Badges that are already there when the page loads just sit still. Press play to see it.
Sizes
xs 20px, sm 24px, md 32px (default), lg 40px and xl 56px. The initials and the status dot scale with them.
Groups
AvatarGroup overlaps a set of avatars, with a ring between them in the colour of what they sit on: the surface by default, or set --avatar-ring on the group for anything else. Hovering one lifts it and shows the name. With max, the rest fold into a "+N" that lists who they are, on hover and to keyboard and screen reader users.
<AvatarGroup max={4} size="sm">
{members.map((m) => <Avatar key={m.id} name={m.name} src={m.photo} />)}
</AvatarGroup>Accessibility
Each avatar is an image named by name, with its status and badge added, like "Ada Okafor, online, 3 notifications". The initials are hidden, so they are never spelled out letter by letter.
Avatars are not interactive and take no focus. The "+N" in a group is focusable, so the hidden names can be reached without a mouse. To make an avatar open a profile, put it inside a Button or a link.
Motion
A photo fades in as it loads. In a group, the hovered avatar lifts by 2px.
The notification badge is the one expressive moment: a notification is not an answer to anything you did, and it is rare, so its arrival plays out over about 650ms, longer than anything else in the library. It is still built only from scale, translate and opacity. Under reduced motion it only fades in, and the number changes without rolling.
API reference
Avatar
Prop
Type
Also takes every prop of Base UI's Avatar.Root. Style it with [data-slot="avatar"].
AvatarGroup
A stack of overlapping avatars. Each shows its name on hover; "+N" lists the rest.
Prop
Type
Also takes every <div> attribute. Style it with [data-slot="avatar-group"].
--avatar-ring: The ring between avatars; set it to the colour the stack sits on. Defaults to the surface.