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.
| Prop | Type | Required | Description |
|---|---|---|---|
| children | ReactNode | No | — |
| variant | AvatarVariant | undefined | No | — |
| size | Size | undefined | No | The diameter, and the type inside it. It sets both sides, never a width alone. |
| radius | RadiusKey | undefined | No | Overrides 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. |
| color | string | undefined | No | A 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. |
| asChild | boolean | undefined | No | R12 — the child element becomes the frame and keeps this variant's style. |
| style | StyleProp<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.
| Prop | Type | Required | Description |
|---|---|---|---|
| children | ReactNode | No | Initials, 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. |
| style | StyleProp<ViewStyle> | No | — |
AvatarImageProps
The node's inherited React Native props apply too, and so do its style props — padding, margin, width and the rest.
| Prop | Type | Required | Description |
|---|---|---|---|
| animation | boolean | undefined | No | `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. |
| style | StyleProp<ImageStyle> | No | — |
AvatarInitialsProps
The node's inherited React Native props apply too, and so do its style props — padding, margin, width and the rest.
| Prop | Type | Required | Description |
|---|---|---|---|
| children | ReactNode | No | — |
Variants, sizes and colour
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
| Legacy | v1 |
|---|---|
<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.