Avatar

Updated: Sep 24, 2026
Avatar renders an identity — a person, a group, an app, or any other subject that is recognised by its picture. It owns its own square dimension, its clip shape, its background treatment, its placeholder glyph size, and the corner slot used for a status indicator or image badge. Supply an image source or custom content plus an accessible label; the component keeps the presentation consistent.
Avatar is the identity primitive behind several other components. AppControlTile, Button, Chip, ContainerHeader, ListItem, and Header embed one and expose a subset of its props, so anything you learn here applies when configuring an avatar through those components.

Usage

import { Avatar, AvatarSize, StatusIndicatorType } from '@wearables-ui-toolkit/mrbd';

type Person = {
  name: string;
  photoUrl?: string;
  isOnline: boolean;
};

export function PersonAvatar({
  person,
  onlineLabel,
}: {
  person: Person;
  onlineLabel: string;
}) {
  return (
    <Avatar
      src={person.photoUrl}
      alt={person.name}
      size={AvatarSize.MEDIUM}
      statusIndicator={person.isOnline ? StatusIndicatorType.ACTIVE : undefined}
      statusIndicatorLabels={{ [StatusIndicatorType.ACTIVE]: onlineLabel }}
    />
  );
}
A duo avatar represents two identities in one slot. Provide a secondary source or secondary content, and the component switches to the duo arrangement:
<Avatar
  src={thread.participants[0].photoUrl}
  secondarySrc={thread.participants[1].photoUrl}
  alt={thread.title}
  size={AvatarSize.LARGE}
/>
An image badge attributes the identity to something else, for example the app a message arrived from:
<Avatar
  src={message.sender.photoUrl}
  alt={message.sender.name}
  badgeImageSrc={message.appIconUrl}
  size={AvatarSize.MEDIUM}
/>
When there is no photo, supply a placeholder glyph and pick the placeholder treatment that matches the subject. A square-ish subject — an album, a thumbnail, a media tile — usually also wants the rounded-rectangle shape:
import {
  Avatar,
  AvatarShape,
  AvatarSize,
  PlaceholderStyle,
  type IconSource,
} from '@wearables-ui-toolkit/mrbd';

export function AlbumArt({
  artworkUrl,
  albumTitle,
  fallbackIcon,
}: {
  artworkUrl?: string;
  albumTitle: string;
  fallbackIcon: IconSource;
}) {
  return (
    <Avatar
      src={artworkUrl}
      alt={albumTitle}
      size={AvatarSize.XLARGE}
      primaryImageShape={AvatarShape.ROUNDED_RECTANGLE}
      placeholderStyle={PlaceholderStyle.IMAGE}
      placeholderIcon={fallbackIcon}
    />
  );
}
Avatars are intrinsically sized, so lay them out with spacing tokens rather than by constraining their box:
.participants {
  display: flex;
  align-items: center;
  gap: var(--uit-spacing-small);
}

Props

The full prop type is AvatarProps. It extends HTMLAttributes<HTMLDivElement>, so standard DOM attributes are forwarded to the root element and ref resolves to that root div. The root element’s role and aria-label are component-owned and cannot be overridden — pass alt instead. Avatar ignores children; use primaryContent, secondaryContent, and badgeContent to fill its slots.
PropTypeDefaultDescription
src
string
—
URL of the primary image. Cropped to fill the slot and not draggable.
secondarySrc
string
—
URL of the secondary image. Supplying it switches the component to the duo layout.
primaryContent
ReactNode
—
Custom node rendered in the primary slot. Takes precedence over src and the placeholder.
secondaryContent
ReactNode
—
Custom node rendered in the secondary slot. Takes precedence over secondarySrc and switches to the duo layout.
alt
string
'Profile Picture'
Accessible label for the identity, and the alt text of the rendered image. The default is an English fallback only; pass a localized string.
placeholderStyle
PlaceholderStyle
PlaceholderStyle.AVATAR
Declares what the primary placeholder stands in for when that slot has neither content nor an image. Does not change what is rendered — use placeholderIcon for that.
placeholderIcon
IconSource
—
Glyph drawn in the primary placeholder. An inline vector glyph is sized from size; a URL asset fills the placeholder surface.
secondaryPlaceholderIcon
IconSource
—
Glyph drawn in the secondary placeholder. Supplying it switches to the duo layout.
size
AvatarSize
AvatarSize.SMALL
Semantic size. Determines the avatar dimension and every derived metric.
primaryImageShape
AvatarShape
AvatarShape.CIRCLE
Clip shape of the primary surface.
avatarStyle
AvatarStyle
AvatarStyle.STANDARD
Background treatment painted behind the image, content, or placeholder.
statusIndicator
StatusIndicatorType
—
Semantic status dot rendered in the corner slot. Takes precedence over the image badge.
statusIndicatorLabels
Partial<Record<StatusIndicatorType, string>>
built-in English labels
Per-status accessible labels appended to the avatar’s accessible name. Keys are partial; omitted statuses fall back to the defaults.
statusIndicatorIcon
IconSource
—
Glyph centered on the status dot, tinted to the primary icon color. Rendered only when statusIndicator is also set.
badgeContent
ReactNode
—
Custom node rendered in the image-badge slot. Takes precedence over badgeImageSrc, and is ignored when statusIndicator is set.
badgeImageSrc
string
—
URL of a full-color, untinted image rendered in the badge slot.
showStroke
boolean
false
Adds the glow stroke overlay that follows the avatar’s shape.
className
string
''
Additional class on the root element.
style
CSSProperties
—
Inline styles merged onto the root element after the component’s own width and height. Use size for sizing, not this prop.

Types and enums

AvatarSize

XXSMALL, XSMALL, SMALL, MEDIUM, LARGE, XLARGE, XXLARGE, XXXLARGE, in ascending order. Every derived metric — avatar dimension, badge diameter, status-dot diameter, badge inset, placeholder glyph size, duo circle size and margin — is chosen from the semantic size, so pick a size rather than reshaping the avatar yourself.

AvatarShape

  • CIRCLE — fully round primary surface, clipped with a circular radius.
  • ROUNDED_RECTANGLE — smooth-corner (squircle) primary surface, clipped with a generated shape path.

AvatarStyle

Selects the background token painted behind the primary slot.
  • STANDARD — --uit-color-background-elevation2.
  • SURFACE — --uit-color-background-surface.
  • TRANSPARENT — --uit-color-background-flat.
  • TRANSLUCENT — --uit-color-background-track.

PlaceholderStyle

Declares what the empty slot stands in for: AVATAR for a person or group identity, IMAGE for generic imagery. The two values render identically today — what the placeholder shows is set by placeholderIcon (and secondaryPlaceholderIcon in the duo layout), so pass the icon you want rather than relying on this value to change the visual.

StatusIndicatorType

ACTIVE, INACTIVE, NOTIFICATION, UNREAD, POSITIVE, NEGATIVE, WARNING, CAUTION. Each maps to the matching --uit-color-persistent-* token, so status color stays consistent across the app. Each also has a built-in English accessible label that you can override through statusIndicatorLabels.

Behavior

Content precedence. In each slot, custom content wins over an image URL, which wins over the placeholder. The primary slot resolves primaryContent, then src, then the placeholder; the secondary slot resolves secondaryContent, then secondarySrc, then the secondary placeholder. A placeholder with no icon renders as an empty background surface rather than a generated monogram — pass placeholderIcon when you want a glyph. Placeholder glyphs inherit --uit-color-text-primary.
Duo layout. Any of secondarySrc, secondaryContent, or secondaryPlaceholderIcon switches the avatar to two overlapping circles: primary at the bottom-left, secondary at the top-right, both on the --uit-color-background-track backdrop. The primary circle punches a cutout out of the secondary one so the two read as distinct identities inside a single avatar footprint. In duo mode the primary image moves into its own circle, so primaryImageShape no longer applies to it.
Corner slot.statusIndicator, badgeImageSrc, or badgeContent all enable the bottom-right slot, and the avatar surface is masked with a matching circular cutout so the badge never sits flush against the image. A status indicator and an image badge are mutually exclusive: when statusIndicator is set, the status dot renders and both badgeContent and badgeImageSrc are ignored. A status dot and an image badge can have different diameters at the same avatar size, and the cutout follows whichever one is rendered. The image-badge slot is clipped to a circle over the --uit-color-background-elevation1 backdrop.
Status glyph.statusIndicatorIcon draws a glyph centered inside the status dot, tinted with --uit-color-icon-primary and inset from the dot’s edge by a proportion the component owns. It renders only when statusIndicator is also set, because the dot is the glyph’s backdrop.
Sizing. The root is an inline-flex box that does not shrink, with a square dimension taken from size. Images fill their slot with object-fit: cover, so non-square sources are cropped rather than letterboxed.
Stroke.showStroke layers a decorative glow stroke over the clipped surface, matched to the circular or rounded-rectangle shape. It is not a border, it does not take pointer events, and it is hidden from assistive technology.
Updates. The component is memoized. Changing size, src, statusIndicator or any other prop re-derives the affected geometry on the next render; there is no imperative API and no internal animation to drive.

Accessibility

  • The root element reports role="img" with an accessible name built from alt, plus the label for statusIndicator when one is set, joined with a comma — for example "Ada Lovelace, Active Status".
  • Pass a localized alt, and localized statusIndicatorLabels, rather than setting aria-label yourself; the component overwrites aria-label on the root.
  • The badge subtree — including the status dot and any badge image — is hidden from assistive technology, so the status reaches screen readers only through the avatar’s accessible name. If a badge image carries meaning, put that meaning in alt or in the parent’s label.
  • Placeholder and badge glyphs are decorative and are marked aria-hidden.
  • When the avatar is decoration inside a labelled parent — a list row, a chip, a container header — let the parent own the accessible name. This avoids announcing the identity twice.
  • Use real imagery for known people and for both halves of a known duo.
  • Choose a semantic AvatarSize and let the component derive every metric.
  • Use the built-in badge and status slots for supplementary meaning.
  • Pass placeholderIcon so an avatar with no photo still reads as a subject.
  • Supply a localized alt, plus localized status labels when the defaults do not fit the product.

Avoid

  • Let Avatar own clipping and its badge cutout; a second clipping element can obscure the built-in mask.
  • Choose a status indicator or image badge because the slots are mutually exclusive.
  • Pair statusIndicatorIcon with statusIndicator; the icon does not render on its own.
  • Choose a supported AvatarSize and prevent stretching parents from resizing it. Width, height, and transform overrides bypass the component’s metrics.
  • Prefer the built-in duo, badge, and stroke behavior over imitating it with stacked circles, borders, or positioned dots.