# Avatar

> A photo that fades in over initials on a color picked from the name, with a presence dot and a story ring whose accent arc sweeps in when there is something new and away once seen; AvatarGroup stacks people with a +N counter and fans out on tap.

- Type: Component (data)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/avatar
- Markdown: https://goodui.dev/native/components/avatar.md
- Registry item: https://goodui.dev/native/r/avatar.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native avatar, profile picture, initials avatar, avatar fallback, online status dot, presence indicator, story ring, instagram stories avatar, avatar group, avatar stack, expo avatar

## When to use

- A person, team or company next to their name: contact rows, comments, chat headers, assignees.
- A story tray: pass story='unseen' or 'seen' and an onPress that opens Stories.
- Presence in messaging or support tools: online, away, busy, offline.
- Showing who is in a meeting, project or event with AvatarGroup.

## When not to use

- A product or content photo: use an Image, or ImageThumbnail from image-viewer when it should open full screen.
- A count or status on its own: use badge.
- Picking people from a list: use chip (input chips with a leading avatar) or a list-row.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/avatar.json
npx expo install --fix
```

The component lands in `components/goodui-native/avatar/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs badge, locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { Avatar, AvatarGroup } from "@/components/goodui-native/avatar/avatar";

export function Assignees() {
  return (
    <>
      <Avatar name="Maya Patel" source="https://example.com/maya.jpg" size="lg" status="online" />
      <AvatarGroup
        max={3}
        people={[
          { name: "Maya Patel", source: "https://example.com/maya.jpg" },
          { name: "Leo Martins" },
          { name: "Aiko Tanaka" },
          { name: "Sam Okafor" },
        ]}
      />
    </>
  );
}
```

## Examples

### Story tray that opens Stories from the tapped avatar

```tsx
const refs = useRef<(ComponentRef<typeof View> | null)[]>([]);
const origin = useRef<ComponentRef<typeof View> | null>(null);

<Avatar
  ref={(el) => { refs.current[i] = el; }}
  name={user.name}
  source={user.avatar}
  size="lg"
  story={allSeen ? "seen" : "unseen"}
  onPress={() => { origin.current = refs.current[i]; setOpen(true); }}
/>
<Stories users={users} open={open} onClose={() => setOpen(false)} initialUser={i} originRef={origin} />
```

### Company logo with a square shape

```tsx
<Avatar name="Northwind" shape="square" source={logo} />
```

### A group that opens the member list instead of fanning out

```tsx
<AvatarGroup people={members} max={4} onPress={() => router.push("/members")} />
```

## API reference

### Avatar

`import { Avatar } from "@/components/goodui-native/avatar/avatar";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | – | The person's name. Gives the initials, the fallback color (always the same for the same name) and the screen reader label. |
| `source` | `AvatarSource` | – | Photo. It fades in over the initials once it has loaded; if it fails the initials stay. |
| `size` | `"sm" \| "md" \| "xs" \| "lg" \| "xl"` | `"md"` | 24, 32, 40, 56 or 80 pt. |
| `shape` | `"circle" \| "square"` | `"circle"` | Circle, or a rounded square for companies, teams and apps. |
| `status` | `"online" \| "away" \| "busy" \| "offline"` | – | Presence dot on the bottom edge: online (green), away (amber), busy (red) or offline (grey). |
| `pulse` | `boolean` | `false` | The online dot breathes outward. Off under reduced motion. |
| `story` | `"unseen" \| "seen"` | – | Story ring around the avatar. `unseen` draws a full accent ring, `seen` a thin grey one. Switching between them sweeps the accent arc in or out around the circle. |
| `ring` | `"background" \| "surface" \| "surfaceMuted" \| "surfaceRaised"` | `"surface"` | Surface the presence dot cuts out of. Match what the avatar sits on. |
| `onPress` | `() => void` | – | Makes the avatar a button (open a profile, play stories). It sinks on touch down inside its ring. |
| `haptic` | `HapticName \| false` | `"tap"` | Haptic on press. `false` turns it off. |
| `accessibilityLabel` | `string` | – | Read by screen readers. Defaults to the name plus status and story state. |
| `ref` | `Ref<ComponentRef<typeof View>>` | – | Ref to the outer view, for example to open Stories from this avatar (`originRef`). |
| `labels` | `Partial<AvatarLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

### AvatarGroup

`import { AvatarGroup } from "@/components/goodui-native/avatar/avatar";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `people` (required) | `AvatarGroupPerson[]` | – | Everyone in the group, in display order. |
| `max` | `number` | `4` | Avatars shown before the rest collapse into a "+N" counter. Default 4. |
| `size` | `"sm" \| "md" \| "xs" \| "lg" \| "xl"` | `"sm"` | – |
| `ring` | `"background" \| "surface" \| "surfaceMuted" \| "surfaceRaised"` | `"surface"` | Surface the overlapping avatars are cut out of. Match what the group sits on. |
| `expandable` | `boolean` | `true` | Tap fans the stack out into a row and back. Default `true`. Ignored when `onPress` is set. |
| `expanded` | `boolean` | – | Fanned out (controlled). |
| `onExpandedChange` | `(expanded: boolean) => void` | – | Called when a tap fans the stack out or folds it. |
| `onPress` | `() => void` | – | Replaces the fan-out with your own action, for example opening the member list. |
| `haptic` | `HapticName \| false` | `"tap"` | Haptic on press. `false` turns it off. |
| `accessibilityLabel` | `string` | – | Read by screen readers. Defaults to "Ana, Ben, Cem and 3 others". |
| `labels` | `Partial<AvatarGroupLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- Without onPress an Avatar is an image labelled with the name, the status and 'new story' (for example 'Maya Patel, online, new story'); the initials and dot are not read separately.
- With onPress it is a button with the same label and a 44 pt minimum touch target through hitSlop.
- AvatarGroup is read as one element: 'Maya Patel, Leo Martins, Aiko Tanaka and 5 others'. When it fans out it is a button with the expanded state (aria-expanded on the web).
- Initials keep their size at large text settings so they never clip; the name is in the label.
- Initials sit on an accent family's soft fill in that family's accent color. Only families that keep 4.5:1 in light and dark are used (violet, blue, green, amber, orange).

## Motion

- The photo fades in over the initials on the slow timing once it has loaded; a photo that fails to load leaves the initials.
- Story ring: switching to unseen sweeps the accent arc around from twelve o'clock over 650 ms; switching to seen retracts it after a short beat, leaving the thin grey ring. Nothing animates on mount.
- A pressable avatar sinks to 0.92 inside its ring on the snappy spring from touch down.
- AvatarGroup fans out on the smooth spring; each avatar starts a beat after the one before it and lifts slightly at the middle of its travel, and the group's width follows on the UI thread.
- The online dot can pulse (Badge pulse).
- Reduced motion: the photo appears without fading, the ring crossfades instead of sweeping, there is no press sink, the group jumps between folded and fanned, and the dot does not pulse.

## Haptics

- tap when a pressable Avatar or AvatarGroup is pressed (configurable with haptic, false turns it off)

## Performance

- The fade, the ring sweep, the press and the fan run on the UI thread with Reanimated. The ring is one SVG circle whose dash offset animates; no re-renders while it moves.
- Color from the name is a small string hash, computed during render with no state.

## Notes for AI

- Always pass name when you know it: it gives the initials, the stable color and the screen reader label.
- source takes a URL string or any React Native image source; do not wrap a URL in { uri } yourself (both work).
- story is 'unseen' | 'seen' | undefined (no ring). Flip it to 'seen' after the person watched the stories; the sweep is the feedback.
- status and pulse are for presence only. Use ring to match the surface the avatar sits on so the dot cuts out cleanly.
- Sizes are named (xs 24, sm 32, md 40, lg 56, xl 80); there is no numeric size prop.
- AvatarGroup fans out by default; pass onPress to replace that, or expandable={false} for a static stack.
- Use avatarAccent(name) and initialsOf(name) if you need the same color or initials elsewhere.

## Related

- [Stories](https://goodui.dev/native/components/stories.md): An Instagram-style story viewer that grows out of the tapped avatar: segmented bars fill at the top, tap right or left to step, hold to pause while the chrome steps aside, swipe sideways to the next person with a plain slide, and drag down to send it back into the avatar.
- [Badge](https://goodui.dev/native/components/badge.md): A count, dot or status badge that springs in when it appears, pops when its value changes, caps at 99+ and shrinks away at zero; BadgeAnchor pins it to an icon or avatar.
- [Chat bubble](https://goodui.dev/native/components/chat-bubble.md): Message bubbles that group into runs with one tail, carry a receipt that ticks forward (clock, a check that draws itself, two checks, both brightening when read) and spring up out of the composer; plus a TypingIndicator whose dots wave and a Composer whose mic morphs into a filled send arrow as soon as there is text.
- [Reaction picker](https://goodui.dev/native/components/reaction-picker.md): Long-press a message and a pill of emoji springs out of it one by one; slide the finger across and the emoji under it swells dock-style while its neighbours lean in; let go to pick and the emoji flies in an arc onto the message, landing in a badge with a pop.

## Guidance for AI tools

Avatar: A photo that fades in over initials on a color picked from the name, with a presence dot and a story ring whose accent arc sweeps in when there is something new and away once seen; AvatarGroup stacks people with a +N counter and fans out on tap. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
