BetaData display

Avatar

A person or a thing, in a circle.

Overview

A person or a thing, in a circle.

First example

<Avatar>AT</Avatar>

Anatomy

<Avatar>
  <Avatar.Image />
  <Avatar.Fallback>
    <Avatar.Initials />
  </Avatar.Fallback>
</Avatar>
  • Avatar — the frame. It resolves the recipe once (R5) and publishes it to its slots.
  • Avatar.Image — the photo. Absolutely positioned over the fallback.
  • Avatar.Fallback — what shows when there is no photo, and what shows behind one that is still loading.
  • Avatar.Initials — the letters. The fallback inserts one around a stringifiable child, so it is rarely written by hand.

Usage

The whole component, most of the time:

<Avatar>AT</Avatar>

With a photo, and initials underneath it:

<Avatar>
  <Avatar.Image source={{ uri: user.photo }} />
  <Avatar.Fallback>AT</Avatar.Fallback>
</Avatar>

A glyph instead of letters — and it needs no props:

<Avatar variant="tertiary">
  <Avatar.Fallback>
    <Icon as={PersonIcon} />
  </Avatar.Fallback>
</Avatar>

A logo, squared:

<Avatar radius="lg" variant="secondary">
  <Avatar.Image source={require('./logo.png')} />
</Avatar>

Root props

AvatarProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo
variantAvatarVariant | undefinedNo
sizeSize | undefinedNoThe diameter, and the type inside it. It sets both sides, never a width alone.
radiusRadiusKey | undefinedNoOverrides the circle. An avatar is round at every size — that is the shape the name means — so this is the prop for the logo or the cover that wants a square.
colorstring | undefinedNoA raw tint (`'#7c3aed'`), never a token (R7). Where it lands follows the variant: the fill of a `primary`, the border of a `tertiary`, the initials of a `ghost`. Its contrasted and soft slices are derived in OKLab, so it behaves exactly like `accent` — which is also why it must be a hex value.
asChildboolean | undefinedNoR12 — the child element becomes the frame and keeps this variant's style.
styleStyleProp<ViewStyle>No

Slots

AvatarFallbackProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNoInitials, an `Icon`, anything. A stringifiable tree becomes `Avatar.Initials` (R3); an element is centred and left alone — and an `Icon` among them needs no props, because the fallback publishes the frame's size and colour to it.
styleStyleProp<ViewStyle>No

AvatarImageProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
animationboolean | undefinedNo`false` shows the image the moment it decodes, with no fade. The frame does not change either way, so a list switched off with this does not reflow.
styleStyleProp<ImageStyle>No

AvatarInitialsProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo

Variants, sizes and colour

This component adds no visual axis of its own. The values it does take are in the generated types above and in the live demo.

Accessibility

No default accessibilityRole: a face is an image, and what a screen reader should read is the name beside it — announcing "image" on every row of a list is noise. Give the avatar an accessibilityLabel when it really is the only label there is.

Avatar.Initials is hidden from the reader for the same reason: two letters standing in for a photo are decoration, and "A T" on every row is noise. Both stay overridable (R9).

Migration from legacy

Legacyv1
<Avatar source={photo} /><Avatar><Avatar.Image source={photo} /></Avatar>
<Avatar name="Amina" /><Avatar>AT</Avatar>
<Avatar size={64} /><Avatar size="lg" />
<Avatar themeColor="success" /><Avatar variant="success" />
<Avatar customAppearance={{ text }} /><Avatar.Initials style={…} /> or its style props

Implementation notes

Notes

The fallback is not a state, it is the layer underneath

Avatar.Image is absolutely positioned over Avatar.Fallback, and an Image with nothing decoded yet draws nothing. So the initials show while the photo loads and stay if the URL is wrong — with no load-state machine, no onError to remember, and nothing to get out of sync. The reference implementation runs a status enum for this; a stacking order says the same thing and cannot disagree with itself.

JSX order between the two is therefore free. Write the image first, as the anatomy reads.

No default glyph

XAUI publishes no icon set — @xaui/icons was deleted in P0 — so the mark is always the caller's. What Avatar.Fallback does instead is publish the frame's resolved size and colour to IconContext, so an Icon written inside it needs no props at all.

The fade

Avatar.Image fades in over 200ms on onLoad — the reference implementation's timing. It runs off a shared value rather than off a mount animation, because the node has to be mounted from the first render or it never fetches: the moment worth animating is the decode, not the mount. animation={false} skips it and mounts no worklet.