# Avatar

> A React avatar: a photo that falls back to colored initials, with a status dot, five sizes, any shape and a stacked avatar group.

- Type: React component (Data Display)
- Access: Free (the source is open to everyone)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add avatar`
- Docs: https://wingo-ui.com/components/avatar

## Interaction

Photos fade in once they load; a slow photo shows its color first and the initials only after a short wait, and a broken one settles on the initials without a flash. A status dot pops in when the status changes. Hover a group, or Tab into it, and the stack fans out on a spring; the avatar under the pointer lifts. On a phone the first tap fans the group out, the next tap on an avatar or on +N opens it, and a tap outside folds it back. With avatar buttons, Tab walks the stack with a focus ring, and when +N reveals the rest in place, focus moves to the first new avatar. Screen readers hear the name and the status, and +N reads as a count.

## Installation

```bash
npx wingo-ui@latest add avatar
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/avatar.json`

Files:

- `components/ui/avatar.tsx`

npm dependencies: `npm i @radix-ui/react-avatar motion lucide-react`

Wingo UI dependencies (installed automatically):

- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Avatar, AvatarGroup } from "@/components/ui/avatar"

const team = [
  { name: "Michael Hayes", src: "/demo/mihai-ionescu.jpg" },
  { name: "Steven Grant" },
  { name: "Jessica Dunn", src: "/demo/ioana-dumitrescu.jpg" },
  { name: "Bianca Ross" },
  { name: "Andrew Porter", src: "/demo/andrei-popescu.jpg" },
]

export function ProfileHeader({ onOpenTeam }: { onOpenTeam: () => void }) {
  return (
    <div className="flex items-center gap-3 rounded-3xl bg-surface p-4 shadow-raised [--avatar-ring:var(--surface)]">
      <Avatar src="/demo/ana-pop.jpg" name="Anna Price" size="lg" status="online" statusLabels={{ online: "Available" }} />
      <div className="min-w-0 flex-1">
        <p className="text-[15px] font-semibold">Anna Price</p>
        <p className="text-[13px] text-muted-foreground">Member since 2019 · San Francisco</p>
      </div>
      <AvatarGroup items={team} max={3} size="sm" label="Team" overflowLabel={(n) => `${n} more`} onOverflowClick={onOpenTeam} />
    </div>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `src?` | `string` | `""` | Image URL. While it loads the avatar shows its fallback color, and if it fails the initials take over. The demo uses /demo/ana-pop.jpg while this is empty; type a broken URL to see the fallback. |
| `name?` | `string` | `""` | The person or organization. It is the spoken label, gives the initials ("Steven Grant" becomes SG) and picks the fallback color. The demo shows Anna Price while this is empty. |
| `alt?` | `string` | `""` | The spoken label when it should differ from name, e.g. "Anna Price, administrator". Empty uses name. The avatar is one image to screen readers: this label plus the status. |
| `size?` | `"xs" \| "sm" \| "md" \| "lg" \| "xl" \| number` | `"md"` | 24 / 32 / 40 / 48 / 64px with initials at 10 / 12 / 14 / 16 / 22px, or any number of px (initials and the status dot scale along). Five sizes on purpose: avatars live in dense lists and on profile headers. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"full"` | full is a circle; sm, md and lg round 16 / 24 / 32% of the size, so every size keeps its shape. Squares suit companies and workspaces. |
| `status?` | `"none" \| "online" \| "offline" \| "busy" \| "away"` | `"none"` | A dot on the bottom end edge: green online, gray offline, red with a bar for busy, amber away. It pops in when it changes and is spoken after the name. |
| `statusLabels?` | `Partial<Record<"online" \| "offline" \| "busy" \| "away", string>>` |  | The spoken status. Defaults to { online: "Online", offline: "Offline", busy: "Busy", away: "Away" }. |
| `variant?` | `"soft" \| "solid"` | `"soft"` | How the fallback is painted: soft is a light tint with deep initials, solid a filled disc with white initials (black on a light custom color). Photos cover either. |
| `colorful?` | `boolean` | `true` | Initials on a color picked from the name: a stable hash into 12 hues, so a person keeps the same color everywhere. The initials are derived from the hue so they keep 4.5:1 in both themes. Off keeps every fallback neutral. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | A fixed fallback color instead of the name hash. Any tone but neutral wins over colorful; neutral leaves the choice to colorful. |
| `color?` | `string` | `""` | Any CSS color for the fallback, a company or team color. Soft tints with it and derives readable initials; solid fills with it as is. Wins over tone and colorful. Leave empty to use them. |
| `ring?` | `boolean` | `false` | A 2px ring in ringColor that cuts the avatar out of whatever it overlaps. Groups turn it on; the demo shows it where the avatar overlaps the cover. |
| `ringColor?` | `string` | `""` | The surface behind the avatar, used for the ring and the status dot outline. Empty inherits a --avatar-ring set by a parent, else var(--background). Pass var(--surface) on panels, or set [--avatar-ring:var(--surface)] once on the panel. |
| `fallbackDelay?` | `number` | `250` | Milliseconds before the fallback shows while a photo loads, so fast photos never flash initials. Only applies with src: a list without photos shows its initials at once. |
| `fallback?` | `ReactNode` |  | Replaces the initials and the user icon: a company logo, an icon, an emoji. |
| `onLoadingStatusChange?` | `(status: "idle" \| "loading" \| "loaded" \| "error") => void` |  | Fires as the photo loads or fails. The root also carries it as data-state. |
| `classNames?` | `Partial<Record<"image" \| "fallback" \| "status", string>>` |  | Extra classes for the photo, the fallback and the status dot. |
| `className?` | `string` |  | Extra classes for the root, merged last. |

## Parts

### AvatarGroup (component)

A stack with a +N chip. items (AvatarProps[]) or Avatar children; max = 4 before +N; total for the real count (+N = total - shown); size = "md" and radius = "full" for every avatar; overlap "sm" | "md" | "lg" (20 / 28 / 36%, default "md"); spread "hover" (default: fans out on hover, keyboard focus or a first tap; the box keeps its size and the fan overflows to the end, so leave room after it) | "always" | "never"; ringColor; onAvatarClick(index) makes avatars buttons; onOverflowClick makes +N a button; overflowLabel = n => `${n} more`; label = "People" (the group's aria-label); classNames { item, overflow }. Props: AvatarGroupProps.

### getInitials (function)

The first letter of the first and last word, Unicode-aware, upper-cased with the ro-RO rules by default: "émile zola" gives ÉZ, one word gives one letter, emoji and punctuation are skipped. Signature: (name: string, locale?: string) => string.

### avatarColor (function)

The fallback color for a name, one of 12 hues (AVATAR_COLORS). Use it to color the same person the same way in lists and charts. Signature: (name: string) => string.

### AVATAR_COLORS (constant)

The 12 fallback hues avatarColor picks from. The initials get an ink derived from each hue, so every one keeps 4.5:1 contrast in both themes.

### AvatarProps (type)

The props of Avatar, for wrappers like a user chip or a member row.

## Examples

- **Sizes**: xs to xl (24 to 64px), photo and initials; initials and the status dot scale along.
- **Radius**: Circle to square. The corners are a share of the size, and the dot stays on the curve.
- **Status in a contacts list**: online, away, busy and offline, spoken with custom statusLabels.
- **Initials palette**: Ten names, ten hues from the name hash. The same person gets the same color everywhere.
- **Fallbacks**: A broken photo settles on the initials, no name shows the user icon, fallback takes any node.
- **Group spread**: Fans out on hover, focus or a first tap; always keeps it apart, never keeps it stacked.
- **Ring color**: The ring cuts avatars out of the surface they sit on. Match it on panels and cards.
- **Soft, solid and custom colors**: Tones and the name hash in both variants, any CSS color as a company mark, and a custom 56px size.

## See also

- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Skeleton](https://wingo-ui.com/components/skeleton) ([Markdown](https://wingo-ui.com/components/skeleton.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))

---

Canonical: https://wingo-ui.com/components/avatar
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
