Bridge view

Avatar

A graphic representation of a user, organisation, or entity — image, initials, or icon — rendered in a bounded surface (circle or square). Carries a fallback chain when the primary representation is missing or fails to load. Often paired with a status indicator and composed into avatar-groups for collaborator lists.

Also called User photo Profile picture Pic

When to use

Use

For representing a user, organisation, or entity in lists, headers, comment threads, attribution lines, and group-membership displays. Pair with a label or name when the avatar is the only marker; pair with the entity's display name when the avatar is decorative reinforcement. The status-indicator slot communicates presence (online / offline / away / busy) for collaborator-aware contexts.

Avoid

For decorative thumbnails that do not represent an entity — that is `Tile` (image-led grid item) or a plain image. For organisation branding above content — that is brand imagery, not avatar. For icon-only buttons — that is `Button` with `Icon`, not Avatar repurposed.

Versus related

  • icon

    `Icon` is the SVG-glyph primitive that may sit inside Avatar as the fallback-icon slot; Avatar is the entity- representation surface. Icon represents an action or concept; Avatar represents an entity.

  • tile

    `Tile` is an image-led grid item for media galleries; Avatar is a bounded entity-representation surface, typically inline with text rather than in a grid. Tile dimensions vary with media aspect ratio; Avatar locks to a square or circle in a canonical size.

  • avatar-group

    `Avatar Group` composes multiple Avatar instances with a bounded `max` count and an overflow indicator; Avatar is the single-entity primitive that Avatar Group composes. When the count is one, render Avatar directly — Avatar Group with one child is an anti-pattern.

Avatar is the canonical user-or-entity-representation primitive — a bounded surface (circle or square) that renders an image, initials, or fallback icon for the represented entity. The fallback chain (image → initials → icon) is deterministic by canonical contract; implementations that pick a different order ship inconsistent surfaces under network failure or missing-data. Five sizes (xs / sm / md / lg / xl), two shapes (circle / square), and an optional status indicator (online / offline / away / busy). The reference documents the slot anatomy, the fallback contract, the accessible- name rules across the three render-modes, and the divergence from Icon and Tile.

Highlight
Fig 1.1 · Avatar · Bridge view

Implementations

How specific libraries realise the canonical anatomy. Each entry records the deltas between the canon and the library's surface.

radix Avatar
import * as Avatar from '@radix-ui/react-avatar';
<Avatar.Root className="AvatarRoot">
<Avatar.Image
className="AvatarImage"
src="https://example.com/photo.jpg"
alt="Alex Black"
onLoadingStatusChange={(status) => console.log(status)}
/>
<Avatar.Fallback className="AvatarFallback" delayMs={600}>
AB
</Avatar.Fallback>
</Avatar.Root>

Divergence

From Type → To Rationale
anatomy[initials] reshaped Avatar.Fallback (accepts any children — initials text, icon element, or any React node) Canonical anatomy separates initials and fallback-icon as two distinct slots in an ordered chain. Radix collapses both into a single Avatar.Fallback that accepts arbitrary children, leaving the initials-vs-icon distinction entirely to the consumer. The library manages only whether Fallback is visible (image loading or error), not which fallback tier is active. Consumers who need the canonical two-rung chain must compose two elements inside a single Avatar.Fallback or conditionally choose content themselves. Source: https://www.radix-ui.com/primitives/docs/components/avatar (fetched 2026-05-05)
anatomy[fallback-icon] omitted Radix has no separate fallback-icon slot. Both the initials rung and the icon rung of the canonical fallback chain map to the same Avatar.Fallback component. Distinguishing between them is the consumer's responsibility via conditional rendering inside Fallback's children prop. Source: https://raw.githubusercontent.com/radix-ui/primitives/main/packages/react/avatar/src/avatar.tsx (fetched 2026-05-05)
anatomy[status-indicator] omitted Radix Avatar has no status-indicator slot. Presence states (online, offline, away, busy) are not modeled in the library at all. Consumers who need a status badge must position a separate element relative to Avatar.Root using CSS; Radix provides no anchor, ARIA wiring, or accessible-label slot for this purpose. Source: https://www.radix-ui.com/primitives/docs/components/avatar (fetched 2026-05-05)
axes.variants[circle] omitted Radix Avatar.Root extends a plain `<span>` with no shape props. The circle shape (border-radius: 50%) is a consumer CSS concern. Radix does not ship a `shape` prop or a variant enumeration. Source: https://www.radix-ui.com/primitives/docs/components/avatar#api-reference (fetched 2026-05-05)
axes.variants[square] omitted Same as circle: Radix has no shape variant. Square shape is consumer CSS. Source: https://www.radix-ui.com/primitives/docs/components/avatar#api-reference (fetched 2026-05-05)
axes.properties[size] omitted Radix Avatar has no size prop. Dimensions are set by consumer CSS on Avatar.Root. The canonical xs/sm/md/lg/xl size scale is a design-system convention layered above the primitive, not a primitive concern. Source: https://www.radix-ui.com/primitives/docs/components/avatar#api-reference (fetched 2026-05-05)
axes.properties[status] omitted Radix Avatar has no concept of presence status. The online/offline/away/ busy enum does not exist in the library. Consumers must implement status as a separate overlay element. Source: https://www.radix-ui.com/primitives/docs/components/avatar#api-reference (fetched 2026-05-05)
axes.properties[hasStatusIndicator] omitted No status indicator exists in Radix Avatar; this boolean control has no corresponding surface in the library. Source: https://www.radix-ui.com/primitives/docs/components/avatar#api-reference (fetched 2026-05-05)
axes.states.data[initials-fallback] reshaped onLoadingStatusChange status='error' or status='loading' with consumer-chosen Fallback children Canonical defines `initials-fallback` as a discrete data state signalling that the initials rung is active. Radix models loading state as ImageLoadingStatus ('idle' | 'loading' | 'loaded' | 'error') surfaced via Avatar.Image's `onLoadingStatusChange` callback. Which visual content (initials or icon) appears inside Avatar.Fallback is driven entirely by the consumer, not the library. There is no Radix-native concept of "initials-fallback" vs "icon-fallback". Source: https://raw.githubusercontent.com/radix-ui/primitives/main/packages/react/avatar/src/avatar.tsx (fetched 2026-05-05)
axes.states.data[icon-fallback] omitted Radix has no icon-fallback state. As with initials-fallback, Fallback visibility is binary (shown or hidden based on image loading status); the content tier within Fallback is not tracked as a library state. Source: https://raw.githubusercontent.com/radix-ui/primitives/main/packages/react/avatar/src/avatar.tsx (fetched 2026-05-05)
events[imageLoad] reshaped onLoadingStatusChange(status='loaded') on Avatar.Image Canonical `imageLoad` fires as a discrete event with payload `{ src, durationMs }`. Radix uses a single `onLoadingStatusChange` callback prop on Avatar.Image that fires on every status transition ('idle' → 'loading' → 'loaded' or 'error'). To match the canonical imageLoad contract, consumers filter for `status === 'loaded'`. The `durationMs` payload field has no Radix equivalent — consumers must record the load-start timestamp themselves. Source: https://raw.githubusercontent.com/radix-ui/primitives/main/packages/react/avatar/src/avatar.tsx (fetched 2026-05-05)
events[imageError] reshaped onLoadingStatusChange(status='error') on Avatar.Image Canonical `imageError` fires with `{ src, fallbackTier }`. Radix surfaces image errors as `status === 'error'` via the same `onLoadingStatusChange` callback. The `fallbackTier` payload field ('initials' | 'icon') has no Radix equivalent — Radix does not distinguish which fallback tier becomes active because it does not model the two-rung chain. Source: https://raw.githubusercontent.com/radix-ui/primitives/main/packages/react/avatar/src/avatar.tsx (fetched 2026-05-05)
motion.durations omitted Radix Avatar ships no motion tokens or animation primitives. The library does not emit data-state attributes on Avatar.Root, Avatar.Image, or Avatar.Fallback (unlike Dialog which exposes data-state for open/closed animation). Consumers who want to animate the fallback transition write CSS directly, outside any Radix-managed surface. Source: https://raw.githubusercontent.com/radix-ui/primitives/main/packages/react/avatar/src/avatar.tsx (fetched 2026-05-05)
motion.reducedMotionFallback omitted Radix Avatar has no reduced-motion handling. No data-state attributes are emitted, so there is no library-managed surface on which prefers-reduced-motion media queries would apply. Motion suppression is entirely outside Radix's scope for this component. Source: https://www.radix-ui.com/primitives/docs/components/avatar (fetched 2026-05-05)
anatomy[initials] extended + Avatar.Fallback gains `delayMs?: number` — a Radix-specific prop absent from the canonical anatomy. When set, Fallback is withheld for the given number of milliseconds; if the image loads within that window, Fallback never renders, eliminating the flash of fallback content on fast connections. Source: https://www.radix-ui.com/primitives/docs/components/avatar#api-reference (fetched 2026-05-05) Radix adds delayMs to solve a practical UX problem: on fast connections, images resolve before the first paint, making a briefly-visible fallback a perceptible flash of unstyled content. The canonical anatomy specifies the fallback chain order but does not model the timing of when each rung becomes visible. Radix handles this at the library layer so consumers do not need to debounce fallback rendering themselves.
Why this audit reads the way it does

Radix Avatar is an unstyled primitive that implements the canonical image- loading and fallback-visibility contract but collapses the canonical two-rung fallback chain (initials → icon) into a single Avatar.Fallback slot. The library's primary contribution is the image-load state machine (idle/loading/loaded/error) and the delayMs flash-prevention mechanism; everything else — shape, size, status indicator, motion — is left entirely to consumers. The most significant divergence is the Fallback collapse: canonical anatomy treats initials and fallback-icon as separate slots to enforce a deterministic order and distinct accessible-name strategies. Radix treats them as one slot with any-children, which means the canonical fallback-chain contract must be re-implemented at the consumer level. The onLoadingStatusChange callback provides the mechanism; the discipline of the chain does not come from the library. Radix Avatar has no ARIA role assignments in its source — accessibility is left entirely to consumers (img alt, role="img", aria-label). This matches the library's "unstyled and accessible-by-consumer-choice" philosophy but means the canonical a11y contracts (role=img on initials/icon variants, aria-label with full entity name) are not enforced by the library.

Both

Figma↔Code mismatches

Where designer and developer worlds typically misalign on this component.

  1. 01
    Figma

    Image and initials drawn as separate component variants

    Code

    A single Avatar component with a fallback chain (image → initials → icon)

    Consequence

    Designers ship "Avatar (image)" and "Avatar (initials)" as separate Figma components; developers ship one component with runtime fallback. The two diverge — designers may not draw the initials variant for every avatar instance, leaving developers without a design reference for the fallback case.

    Correct

    Model the fallback chain as a single Avatar component in both Figma and code. Document each fallback rung as a state in the canonical anatomy (image-loaded / initials-fallback / icon-fallback). Designers draw all three states for at least one representative avatar; developers ship the chain runtime.

  2. 02
    Figma

    Status indicator drawn as a separate floating element with no relationship

    Code

    Status indicator anchored to the avatar with logical-property positioning

    Consequence

    Designers place a coloured dot near the avatar with absolute coordinates; developers ship `inset-inline-end` anchoring that flips under RTL. The two surfaces look the same in LTR but diverge under RTL when the status indicator stays anchored to the wrong edge.

    Correct

    Document the status indicator as a slot of the Avatar component, anchored via logical properties (`inset-inline-end`, `inset-block-end`). Both Figma and code express the slot as a child of the container, not a sibling.

  3. 03
    Figma

    Initials baked into the Figma component as static text "AB"

    Code

    A controlled `name` prop drives the initials computation

    Consequence

    Designers ship static "AB" in every initials-variant frame; developers compute initials from a `name` prop at render time. The Figma file does not document the computation algorithm (first letter? first two? first letter of each word?) so developers reinvent it.

    Correct

    Document the canonical initials algorithm in the anatomy (`first letter of each word, max two`). The Figma file uses placeholder strings for design illustration; the algorithm lives in code with the `name` prop driving it.

  4. 04
    Figma

    Avatar size hard-coded as a px value

    Code

    Size driven by a token (`xs / sm / md / lg / xl`)

    Consequence

    Designers set `width: 24px` directly on the avatar frame; developers bind size to a token. When the size scale rebalances, the Figma file lags and the shipped avatar visually disagrees with the surrounding text.

    Correct

    Bind size to a token in both surfaces. Figma's size component- property maps to `xs / sm / md / lg / xl`; code uses the same enum.

Both

Variants, properties, states

Variants

Structurally different versions of the component.

circle square

Properties

The same component, parameterised.

PropertyType
size xs | sm | md | lg | xl
status online | offline | away | busy
hasStatusIndicator boolean

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visible
data
image-loadingimage-loadedimage-errorinitials-fallbackicon-fallback
Both

Figma ↔ Code property map

FigmaKindCodeNotes
VariantEnumshapeMaps circle / square shape variant.
SizeEnumsizeMaps xs / sm / md / lg / xl.
StatusEnumstatusMaps online / offline / away / busy presence indicator.
Has Status IndicatorBooleanhasStatusIndicator
NameTextnameDrives initials computation and accessible-name fallback.
SourceTextsrcImage URL; null or undefined activates initials fallback.
ImageSlotimage
Fallback IconSlotfallback-icon
Designer

Figma anatomy

Slot Figma type Hint
container frame Auto-layout fixed-size frame; aspect-ratio 1:1; border-radius bound to shape variant
image frame Image fill on the container frame; aspect 1:1; object-fit cover
initials text Centred text inside the container; size bound to avatar size token
fallback-icon instance Icon component instance; size bound to avatar size token
status-indicator frame Small circle anchored to container's bottom-right; colour bound to status enum
Dev

Code anatomy

Slot Code slot Semantic
container container span-or-figure
image image img
initials initials text
fallback-icon fallback-icon presentational
status-indicator status-indicator span
Dev

Cross-framework expression

FrameworkStructure mechanismVariant mechanism
Web Components A `<ui-avatar>` host with `name`, `src`, `size`, `shape`, `status` attributes; renders image / initials / icon based on internal state machine attributes (`shape="circle"`, `size="md"`, `status="online"`); CSS @media respects prefers-reduced-motion
React Function component (Radix Avatar, MUI Avatar, Mantine Avatar, Chakra UI Avatar) wrapping a span / figure; library convention varies between compound components (Avatar.Image / Avatar.Fallback) and prop-driven render props with class-variance-authority for shape / size; controlled `src` + `name` + `status` props; fallback chain handled by `<Avatar.Image onError>` plus `<Avatar.Fallback>` (Radix idiom)
Angular (signals) Angular component with input<string>('name'), input<string | null>('src'), signal-based loading state for fallback chain input<'circle' | 'square'>(); input<'xs' | 'sm' | 'md' | 'lg' | 'xl'>(); host-binding [attr.aria-label] driven by name signal
Vue Single-file component with v-bind for src / name / status; PrimeVue Avatar, Naive UI Avatar as third-party precedents defineProps with literal-union types; v-if for fallback chain rendering
Both

Events

  1. imageLoadoptional
    Payload
    `{ src: string, durationMs: number }`. Fires when the image successfully loads. Only canonical when consumers want load-timing telemetry; static avatars never observe it.
    Web Components
    `imageLoad` CustomEvent on the host with `event.detail = { src, durationMs }`.
    React
    `onImageLoad(src, durationMs)` callback; or derived from a controlled `loading` prop transition.
    Angular Signals
    `output<{ src: string; durationMs: number }>('imageLoad')`.
    Vue
    `@image-load` event with payload `{ src, durationMs }`.
  2. imageErroroptional
    Payload
    `{ src: string, fallbackTier: 'initials' | 'icon' }`. Fires when the image fails to load and the fallback chain advances. Only canonical when consumers want error telemetry or want to report broken-link-rate per source.
    Web Components
    `imageError` CustomEvent with `event.detail = { src, fallbackTier }`.
    React
    `onImageError(src, fallbackTier)` callback.
    Angular Signals
    `output<{ src: string; fallbackTier: AvatarFallbackTier }>('imageError')`.
    Vue
    `@image-error` event with payload `{ src, fallbackTier }`.
Both

Internationalisation

RTL · mirroring

Status indicator anchors to the bottom-end edge via `inset-inline-end` / `inset-block-end`, so it flips from bottom-right (LTR) to bottom-left (RTL) automatically. Initials computation respects the writing direction — for LTR-name "Alex Black" the initials are "AB"; for RTL-name computation the same letter-extraction rule applies but the visual order reverses (browser-handled when initials are rendered as text content).

Text expansion

Initials do not text-expand (always 1-2 characters). Status-indicator `aria-label` text follows i18n string- expansion (German "online" → "online" stable, but "unavailable" → "nicht verfügbar" 60 % longer for SR). Avatar dimensions are size-token-driven; no width-reservation needed for text expansion.

Both

Accessibility

Slot Accessibility hint
container Container's role depends on the render-mode. Image variant wraps `<img>` carrying `alt`. Initials / icon variants set `role="img"` plus `aria-label` describing the entity (the person's name, the organisation, etc.). Decorative avatars (paired with a visible name in the surrounding layout) set `aria-hidden="true"` and let the host's name carry the accessible name.
image `<img alt="">` for decorative avatars (host carries the name) or `<img alt="<entity name>">` for meaningful avatars. Never leave alt undefined — that defaults to the file path being announced in some screen readers.
initials When initials carry the entity name (no surrounding label), the container carries `role="img"` plus `aria-label` with the full entity name — never just the initials, which are ambiguous to SR users (`"AB"` is meaningless without context).
fallback-icon Decorative when the host carries the entity name. When the icon is the entire avatar content, the container carries `role="img"` plus `aria-label` with the entity name (or "Unknown user" / "Anonymous" placeholder if the entity is genuinely unknown).
status-indicator Status indicator carries `aria-label` with the status word ("online", "offline", "away", "busy") so SR users hear it alongside the avatar's accessible name. Without the label, the coloured dot is meaningless to AT.
Both

Accessibility acceptance

Keyboard walk

KeysExpected
Tab (decorative avatar)Decorative avatars are not in the tab order. Focus moves through the host element (button, link, list-item).
Tab (interactive avatar, rare)Interactive avatars (used as a profile-link) participate as a single tab stop. Focus ring renders on the container. Hit target ≥ 24×24 px (WCAG 2.5.8) — small avatar sizes (xs / sm) need explicit padding to meet the threshold when clickable.

Screen-reader announcements

TriggerExpected
SR encounters an avatar with image variantSR announces the `<img>` alt text. For decorative avatars with `alt=""`, SR is silent and the host's name reads.
SR encounters an avatar with initials / icon variantSR announces the container's `role="img"` accessible name ("Alex Black"). The initials letters or icon glyph are decorative; AT reads the entity-name only.
SR encounters an avatar with status indicatorSR announces avatar name plus status word ("Alex Black, online"). Status follows the avatar in DOM and reading order.

axe-core rules to assert

  • image-alt
  • aria-allowed-attr
  • aria-required-attr
  • role-img-alt
  • color-contrast

Same data as JSON for direct ingestion into Playwright + @axe-core/playwright or Jest + jest-axe: /api/components/avatar/a11y-fixture.json

Both

Contracts

Non-negotiable contracts

  1. Canon

    Fallback chain is image → initials → icon, in that order. Image rung activates when `src` is present and the load succeeds; initials rung activates when `name` is present and the image rung failed; icon rung is the ultimate fallback. Implementations that pick a different order ship inconsistent avatar surfaces under network failure or missing-data.

    Without a deterministic order, the same entity may appear as initials in one surface and as the generic icon in another; the avatar's identity becomes unstable. Users lose trust in the avatar as a reliable entity-marker.

  2. APGWAI-ARIA role=img + img alt patterns

    Initials and icon variants carry `role="img"` plus `aria-label` with the full entity name on the container. Image variants use `<img alt>` for the same purpose. The accessible name is always the entity name — never the initials letters, never the icon glyph name, never the file path.

    Without the entity-name accessible-name, SR users hear "image" or "AB" — neither identifies the entity. The avatar becomes a sighted-only marker. Decorative avatars escape the rule by setting `aria-hidden="true"` and relying on the host's name; meaningful avatars must carry the name themselves.

  3. Canon

    Status indicator carries `aria-label` with the status word ("online", "offline", "away", "busy"). The coloured dot is sighted-only; AT needs the word.

    Without the label, presence is invisible to SR users — critical for collaborator-aware contexts where presence determines whether to message synchronously or asynchronously.

Vocabulary drift

Material 3
Avatar
Polaris
Avatar
Carbon
User Avatar
Atlassian
Avatar
Radix
Avatar
Radix exposes the fallback chain as compound subcomponents (`Avatar.Image` + `Avatar.Fallback`); other libraries ship a single component with internal state. Either expression of the same canonical contract.
Both

Common mistakes

Blocker

#avatar-no-fallback-order

Fallback chain order undefined or random

Problem

The implementation falls back to initials when the image fails OR to the icon — depending on environment, network, or library version. Users see different fallback content for the same entity across surfaces; the avatar's identity becomes unstable.

Fix

Document the fallback order as image → initials → icon in the canonical anatomy. The image rung activates when the `src` is present and the load succeeds; the initials rung activates when `name` is present and the image rung failed; the icon rung activates when both prior rungs failed. Pick one and never diverge.

Blocker

#avatar-no-aria-label

Initials or icon variant ships without accessible name

Problem

The avatar renders initials "AB" or a generic person icon, but the container has no `aria-label` and the host has no visible label. SR users encounter "image" with no entity-name; the avatar is meaningless to AT.

Fix

Set `role="img"` plus `aria-label` with the full entity name on the container for initials / icon variants. Image variant uses `<img alt="<entity name>">`. Decorative avatars (paired with a visible name in the layout) set `aria-hidden="true"` and let the host's name carry the accessible name.

Major

#avatar-status-no-label

Status indicator dot has no accessible label

Problem

The status indicator renders a coloured dot anchored to the avatar's corner. SR users hear the avatar's name but no status cue; the presence-information is sighted-only.

Fix

The status indicator carries `aria-label` with the status word ("online", "offline", "away", "busy"). SR announces the status alongside the avatar's name. Document the canonical status vocabulary so every implementation announces consistently.

Major

#avatar-initials-only-as-label

Initials are the entire accessible name

Problem

The container's `aria-label="AB"` — just the initials. SR reads "image, AB" with no entity context. Initials are visual shorthand; AT needs the full name.

Fix

`aria-label` carries the full entity name ("Alex Black") even when the visual is just the initials. The initials are visual only; the accessible name is the canonical source of truth.

Major

#avatar-image-no-alt

Image variant has no alt attribute

Problem

The `<img>` element has no `alt` attribute (different from `alt=""`). Some SR announce the file path or URL; others silently skip the avatar entirely.

Fix

Always set `alt` on the `<img>`. `alt=""` for decorative avatars (host carries the name); `alt="<entity name>"` for meaningful avatars without a surrounding label. Never leave alt undefined.