# Avatar

A cross-platform React avatar component that displays a user image, icon, or text initials. Supports notification badges and grouping via `AvatarGroup`.
<!-- BEGIN:xui-mcp-instructions:avatar -->
A visual representation of a user or entity. Supports three content types — initials, icon, and image — across six sizes and two tonal variants. Can be circular or square, and optionally interactive with a hover state.

### When to use

- To identify a user in a profile header, comment thread, or account menu
- In lists, tables, or cards where a visual identity marker helps scan rows quickly
- In AvatarGroup to represent multiple users in a compact space
- As a touch target for opening a user profile or account dropdown

### When not to use

- As a generic decorative image — use an IconWrapper component instead
- When the entity being represented is not a person or named account (e.g. a product, category, or abstract concept)
- When you need a logo — use a BrandLogo or Icon Wrapper with a brand asset instead

### Content guidelines

Text type: use the first letter of the first name and the first letter of the last name (e.g. *"J"* or *"JD"*). Never use more than two characters.

Image type: always use a square source image; the component clips it to fit. Avoid images with important content near the edges.

Alt text: always provide a meaningful aria-label or alt string for the Avatar — e.g. the user's full name — regardless of content type.

### Behaviour guidelines

Fallback chain — always define a fallback order for content type: attempt to load Image first; if the image fails to load or is unavailable, fall back to Text (initials); if the name is also unknown, fall back to Icon. Never leave the container empty.

Image loading — while the photo is loading, render the Icon variant as a placeholder. Avoid layout shift by reserving the Avatar's dimensions before the image resolves.

Interactive vs decorative — an Avatar that opens a profile, menu, or action sheet must have Hover=True and receive keyboard focus. An Avatar used purely for identification (e.g. in a comment or log entry) must not be focusable and must not have hover styling.

Click / tap target — when interactive, the entire Avatar container is the tap target. Do not wrap only the image or icon in a link while leaving the container unresponsive.

Tooltip on hover — where the initials or icon may be hard to identify, show a tooltip with the user's full name on hover and on focus.

Badge visibility — the Badge reflects live status data (e.g. online / offline). It should update in real time without a full component remount. If status data is unavailable, hide the Badge (Badge=False) rather than showing a stale or unknown state.

Square vs circle — do not switch shape dynamically for the same entity. The shape is a semantic signal (circle = person, square = bot/service) and must remain stable across contexts where the same Avatar appears.

### Accessibility

Provide aria-label with the user's name on every Avatar instance.

If the Avatar is interactive, it must receive keyboard focus and respond to Enter / Space.

Use role=*"img"* for non-interactive Avatars and role=*"button"* for interactive ones.

Do not rely on color (Tone) alone to convey meaning — pair it with a label or tooltip where necessary.
<!-- END:xui-mcp-instructions:avatar -->

## Installation

```bash
npm install @xsolla/xui-avatar
```

## Imports

```tsx
import {
  Avatar,
  AvatarGroup,
  type AvatarGroupItem,
  type AvatarBackgroundMode,
} from "@xsolla/xui-avatar";
```

## Quick start

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";

export default function QuickStart() {
  return <Avatar text="JD" aria-label="John Doe" />;
}
```

## API Reference

### `<Avatar>`

| Prop         | Type                                                                                                    | Default   | Description                                                                                                   |
| ------------ | ------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------- |
| `testID`     | `string`                                                                                                | —         | Test ID for testing frameworks. On web this renders as `data-testid`; on React Native it renders as `testID`. |
| `src`        | `string`                                                                                                | —         | Image source URL.                                                                                             |
| `icon`       | `ReactNode`                                                                                             | —         | Icon displayed when no `src` is supplied.                                                                     |
| `text`       | `string`                                                                                                | —         | Text/initials displayed when no `src` or `icon` is supplied.                                                  |
| `size`       | `"xl" \| "lg" \| "md" \| "sm" \| "xs" \| "xxs"`                                                         | `"xl"`    | Avatar size.                                                                                                  |
| `square`     | `boolean`                                                                                               | `false`   | Square (small radius) instead of circular.                                                                    |
| `tone`       | `"mono" \| "brand"`                                                                                     | `"mono"`  | Visual tone.                                                                                                  |
| `stroke`     | `boolean`                                                                                               | `true`    | Render the avatar's outer border. Set `false` to remove the border entirely.                                  |
| `badge`      | `boolean`                                                                                               | `false`   | Show a notification badge.                                                                                    |
| `badgeCount` | `ReactNode`                                                                                             | —         | Numeric or text content rendered inside the badge.                                                            |
| `badgeIcon`  | `ReactNode`                                                                                             | —         | Icon rendered inside the badge.                                                                               |
| `badgeTone`  | `"primary" \| "secondary" \| "brand" \| "brandExtra" \| "success" \| "warning" \| "alert" \| "neutral"` | `"alert"` | Badge colour tone.                                                                                            |
| `aria-label` | `string`                                                                                                | —         | Accessible label. Recommended for screen readers.                                                             |
| `alt`        | `string`                                                                                                | —         | Alt text for the image (falls back to `aria-label`).                                                          |
| `onClick`    | `() => void`                                                                                            | —         | Click handler. When provided, the avatar becomes a focusable button.                                          |

Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).

`backgroundColor` and `disableHover` exist on the source type but are reserved for internal use by `AvatarGroup` (`@internal`); avoid setting them directly.

### `<AvatarGroup>`

| Prop                   | Type                                                                                                          | Default   | Description                                                             |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------- |
| `list`                 | `AvatarGroupItem[]`                                                                                           | —         | **Required.** Avatars to display.                                       |
| `size`                 | `"xxs" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"`                                                               | `"sm"`    | Size applied to every avatar.                                           |
| `maxVisible`           | `number`                                                                                                      | `6`       | Maximum slots rendered, including the "+N" overflow counter.            |
| `avatarBackgroundMode` | `"mixed" \| "brand" \| "brandExtra" \| "success" \| "warning" \| "alert" \| "neutral" \| ((theme) => string)` | `"mixed"` | Background colour mode for non-image avatars.                           |
| `aria-label`           | `string`                                                                                                      | auto      | Accessible label for the group. Defaults to "X users" / "N of M users". |

Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).

### `AvatarGroupItem`

```typescript
interface AvatarGroupItem {
  src?: string;
  initials?: string;
  tooltip?: string;
  badge?: boolean;
  badgeCount?: React.ReactNode;
  badgeIcon?: React.ReactNode;
  badgeTone?: AvatarProps["badgeTone"];
}
```

## Examples

### Sizes

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";

export default function AvatarSizes() {
  return (
    <div style={{ display: "flex", gap: 16, alignItems: "center" }}>
      <Avatar size="xxs" text="A" />
      <Avatar size="xs" text="A" />
      <Avatar size="sm" text="A" />
      <Avatar size="md" text="A" />
      <Avatar size="lg" text="A" />
      <Avatar size="xl" text="A" />
    </div>
  );
}
```

### Tones

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";

export default function AvatarTones() {
  return (
    <div style={{ display: "flex", gap: 16 }}>
      <Avatar tone="mono" text="JD" />
      <Avatar tone="brand" text="JD" />
    </div>
  );
}
```

### Borderless

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";

export default function BorderlessAvatar() {
  // stroke defaults to true; pass stroke={false} to remove the outer border
  return <Avatar text="JD" stroke={false} />;
}
```

### With badge

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
import { Bell } from "@xsolla/xui-icons-base";

export default function AvatarWithBadge() {
  return (
    <div style={{ display: "flex", gap: 24 }}>
      <Avatar text="JD" badge />
      <Avatar text="JD" badge badgeCount={5} />
      <Avatar text="JD" badge badgeIcon={<Bell />} badgeTone="brand" />
    </div>
  );
}
```

### Square

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";

export default function SquareAvatar() {
  return <Avatar text="AB" square size="lg" />;
}
```

### Custom icon

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
import { Briefcase } from "@xsolla/xui-icons-base";

export default function AvatarWithIcon() {
  return <Avatar icon={<Briefcase />} tone="brand" aria-label="Work account" />;
}
```

### Clickable

```tsx
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";

export default function ClickableAvatar() {
  const [count, setCount] = React.useState(0);
  return (
    <Avatar
      text="JD"
      size="lg"
      onClick={() => setCount((c) => c + 1)}
      aria-label={`Open profile (clicked ${count} times)`}
    />
  );
}
```

### Avatar group

```tsx
import * as React from "react";
import { AvatarGroup, type AvatarGroupItem } from "@xsolla/xui-avatar";

export default function AvatarGroupExample() {
  const users: AvatarGroupItem[] = [
    { initials: "JD", tooltip: "John Doe" },
    { initials: "AB", tooltip: "Anna Brown" },
    { initials: "CD", tooltip: "Carl Davis" },
    { initials: "EF", tooltip: "Emma Frost" },
    { initials: "GH", tooltip: "Grace Hill" },
    { initials: "IJ", tooltip: "Ivan Jones" },
    { initials: "KL", tooltip: "Kim Lee" },
  ];

  return <AvatarGroup list={users} maxVisible={5} size="md" />;
}
```

## Accessibility

- Pass `aria-label` so screen readers can identify the avatar; `alt` falls back to it for images.
- When `onClick` is set the root receives `role="button"`, `tabIndex={0}`, and Enter/Space activation.
- `AvatarGroup` is a `role="group"` with an auto-generated label such as "5 of 12 users".
- The "+N" overflow counter announces "N more user(s)" via `aria-label`.
