Data Display

Avatar

An avatar that shows initials until the image loads and then crossfades, with a breathing presence dot, a hover ring that expands, five sizes and two shapes.

The avatar you need for member lists, comment threads and account menus. The image preloads after mount; until then the avatar shows initials derived from alt — the last two characters of a Chinese name, or the first letters of the first two words of a Latin name — on a background picked from six neutral warm-grey tones by a deterministic hash of the name, so the same person always gets the same tone. When the image finishes loading the two layers crossfade; if it fails, the initials stay. A presence dot can sit in the bottom-right corner and breathes slowly when online. With ring on, hovering the avatar or focusing a wrapping link expands an outline out from its edge.

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

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

200
Active edited usageDefault props
<Avatar/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/avatar.en.json

Or, once registries are configured in components.json, install it as @webberui/avatar.

Usage

import { Avatar } from "@/components/ui/avatar";

<Avatar src="/team/yuqing.jpg" alt="林雨晴" status="online" ring />

{/* No image: shows "雨晴" */}
<Avatar alt="林雨晴" size="lg" shape="rounded" />

{/* Inside a link, focus shows the ring too */}
<a href="/members/yuqing" className="rounded-full focus-visible:outline-none">
  <Avatar src="/team/yuqing.jpg" alt="林雨晴" ring delayMs={300} />
</a>

Props

PropTypeDefaultDescription
srcstring—Image URL; the initials show when omitted or when loading fails
altstring—Required. Alt text for the image, and the source of the initials and background tone
fallbackReactNode—Overrides the generated initials; accepts any node
size"xs" | "sm" | "md" | "lg" | "xl""md"24 / 32 / 40 / 56 / 72px
shape"circle" | "rounded""circle"A circle or a square with 22% rounded corners
status"online" | "busy" | "away" | "offline"—Presence dot in the bottom-right corner; hidden when omitted
statusLabelstringBy status: "Online" / "Busy" / "Away" / "Offline"Status text for screen readers
ringbooleanfalseShows an expanding outline on hover or when a wrapper is focused
durationnumber200Crossfade and ring transition duration (ms), written to --wb-avatar-duration
delayMsnumber0How long to wait before showing the initials when src is set (ms); 300 avoids a flash of initials when images usually hit the cache
onLoadingStatusChange(status: "idle" | "loading" | "loaded" | "error") => void—Called whenever the image loading status changes
classNamestring—Passed to the outermost element

getInitials(alt) is also a named export, so other places (a text avatar in a dropdown, say) can reuse the same initials rule.

How it works

  • Initials rule: names containing CJK ideographs use the last two ideographs ("林雨晴" → "雨晴"; one for a single-character name), because Chinese speakers address people by given name; other names use the uppercased first letters of the first two words ("Maya Lin" → "ML").
  • Preloading runs with new Image() only after mount. The server render always outputs the initials layer, matching the first client render, so there is no hydration warning.
  • The image layer and the initials layer are absolutely positioned in the same box, so the layout does not jump during the crossfade.
  • The presence dot is 25% of the avatar, with a 2px outer ring in the background colour. Away is drawn as a crescent with a CSS mask, so the cut-out is truly transparent; offline is a hollow circle. Only online pulses (the halo scales to 1.8× and fades out, every 2 seconds).
  • Timing is exposed as --wb-avatar-duration (default 200ms) and --wb-ease-out, both overridable in global styles.

Accessibility

  • The image uses alt; before it loads, the initials layer is role="img" with the full name as its aria-label
  • With a status, sr-only text such as "(Online)" is appended, so screen readers announce "Maya Lin (Online)"; the dot and the pulse are aria-hidden
  • The ring responds to hover and to focus-visible on a wrapping link or button, so keyboard users can see where focus is
  • When the user's system has reduce motion turned on, the crossfade becomes an instant swap and the online dot does not pulse