Quire UI
Data display3

Avatar

Photos with initials that keep their colour per person, presence dots, and stacks that name everyone.

Billing team

7 people

+4
  • Ada Okafor

    Owner

    online
  • Lena Brandt

    Admin

    away
  • Tomás Reyes

    Member

    busy
  • Mei Tanaka

    Member

    offline

Installation

npx shadcn@latest add @quireui/avatar

Usage

<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.

Unassigned
Ada Okafor

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.

+3
<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.

On this page