> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.

# Avatar

A person's photo on a rounded plate, falling back to initials or an icon while it loads, when it fails, or when there is none. It is decorative beside a visible name; standing alone, give it `aria-label` so it reads as an image. Add `AvatarContent` with `AvatarName` (and an optional `AvatarDescription`) for the user item: plate and name in one row. To let people change it, make the avatar the `FileUploadTrigger` (`asChild`) and give it `as='button'` — picking only previews, and your form uploads.

[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Avatar) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/data-display-avatar--overview)

```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';

export const Basic = () => (
  <Avatar data-testid='avatar'>
    <AvatarImage src={PHOTO} />
    <AvatarFallback name='Ada Lovelace' />
  </Avatar>
);
```

## Usage

```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
```

## Examples

### Sizes

`sm` (20px) and `md` (24px, the nav-rail plate) are the compact sizes for user items; `lg` (32px,
the default) is for rows and forms. Each row shows a photo, initials and the icon, then the same
three inside a Branded frame.

```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';

export const Sizes = () => (
  <VStack gap={16} align='start' data-testid='avatar'>
    {SIZES.map(size => (
      <HStack key={size} gap={12} align='center' data-testid={`avatar-${size}`}>
        <Avatar size={size} data-testid={`avatar-${size}-photo`}>
          <AvatarImage src={PHOTO} />
          <AvatarFallback name='Ada Lovelace' />
        </Avatar>
        <Avatar size={size} data-testid={`avatar-${size}-initials`}>
          <AvatarFallback name='Ada Lovelace' />
        </Avatar>
        <Avatar size={size} data-testid={`avatar-${size}-icon`}>
          <AvatarFallback />
        </Avatar>
        <HStack gap={12} align='center' data-frame-style='branded'>
          <Avatar size={size} data-testid={`avatar-${size}-branded-photo`}>
            <AvatarImage src={PHOTO} />
            <AvatarFallback name='Ada Lovelace' />
          </Avatar>
          <Avatar size={size} data-testid={`avatar-${size}-branded-initials`}>
            <AvatarFallback name='Ada Lovelace' />
          </Avatar>
          <Avatar size={size} data-testid={`avatar-${size}-branded-icon`}>
            <AvatarFallback />
          </Avatar>
        </HStack>
      </HStack>
    ))}
  </VStack>
);
```

### With name

The user item (Figma 12557:5856): the plate and the name in one row, 6px apart. The name is
`text-sm` at every size and truncates with an ellipsis when the row is narrow.

```tsx
import { Avatar, AvatarContent, AvatarFallback, AvatarImage, AvatarName } from '@wallarm-org/design-system/Avatar';
import { VStack } from '@wallarm-org/design-system/Stack';

export const WithName = () => (
  <VStack gap={16} align='start' data-testid='avatar'>
    {(['sm', 'md'] as const).map(size => (
      <VStack key={size} gap={8} align='start' data-testid={`avatar-${size}`}>
        <Avatar size={size} data-testid={`avatar-${size}-photo`}>
          <AvatarImage src={PHOTO} />
          <AvatarFallback name='Artem Miskevich' />
          <AvatarContent>
            <AvatarName>Artem Miskevich</AvatarName>
          </AvatarContent>
        </Avatar>
        <Avatar size={size} data-testid={`avatar-${size}-initials`}>
          <AvatarFallback name='Artem Miskevich' />
          <AvatarContent>
            <AvatarName>Artem Miskevich</AvatarName>
          </AvatarContent>
        </Avatar>
        <div data-frame-style='branded'>
          <Avatar size={size} data-testid={`avatar-${size}-branded`}>
            <AvatarFallback name='Artem Miskevich' />
            <AvatarContent>
              <AvatarName>Artem Miskevich</AvatarName>
            </AvatarContent>
          </Avatar>
        </div>
        <div className='w-120'>
          <Avatar size={size} data-testid={`avatar-${size}-truncated`}>
            <AvatarFallback name='Maximilian Alexander-Montgomery' />
            <AvatarContent>
              <AvatarName>Maximilian Alexander-Montgomery</AvatarName>
            </AvatarContent>
          </Avatar>
        </div>
      </VStack>
    ))}
  </VStack>
);
```

### With description

`AvatarDescription` adds a secondary line under the name — the email in the account menu. Both lines truncate.

```tsx
import { Avatar, AvatarContent, AvatarDescription, AvatarFallback, AvatarImage, AvatarName } from '@wallarm-org/design-system/Avatar';
import { VStack } from '@wallarm-org/design-system/Stack';

export const WithDescription = () => (
  <VStack gap={16} align='start' data-testid='avatar'>
    <Avatar data-testid='avatar-photo'>
      <AvatarImage src={PHOTO} />
      <AvatarFallback name='Artem Miskevich' />
      <AvatarContent>
        <AvatarName>Artem Miskevich</AvatarName>
        <AvatarDescription>admin@wallarm.com</AvatarDescription>
      </AvatarContent>
    </Avatar>
    <Avatar size='md' data-testid='avatar-md'>
      <AvatarFallback name='Artem Miskevich' />
      <AvatarContent>
        <AvatarName>Artem Miskevich</AvatarName>
        <AvatarDescription>admin@wallarm.com</AvatarDescription>
      </AvatarContent>
    </Avatar>
    <div className='w-160'>
      <Avatar data-testid='avatar-truncated'>
        <AvatarFallback />
        <AvatarContent>
          <AvatarName>Maximilian Alexander-Montgomery</AvatarName>
          <AvatarDescription>maximilian.alexander-montgomery@wallarm.com</AvatarDescription>
        </AvatarContent>
      </Avatar>
    </div>
  </VStack>
);
```

### Fallback

Initials are the first letters of the first and last word of `name`; without a name the person icon shows, whether the photo is missing or failed to load.

```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { HStack } from '@wallarm-org/design-system/Stack';

export const Fallback = () => (
  <HStack gap={12} align='center' data-testid='avatar'>
    <Avatar>
      <AvatarFallback name='Grace Hopper' />
    </Avatar>
    <Avatar>
      <AvatarFallback />
    </Avatar>
    <Avatar data-testid='avatar-broken-initials'>
      <AvatarImage src='/does-not-exist.png' />
      <AvatarFallback name='Alan Turing' />
    </Avatar>
    <Avatar data-testid='avatar-broken-icon'>
      <AvatarImage src='/does-not-exist.png' />
      <AvatarFallback />
    </Avatar>
  </HStack>
);
```

### Custom icon

`icon` replaces the default person glyph whenever `name` gives no initials.

```tsx
import { Avatar, AvatarFallback } from '@wallarm-org/design-system/Avatar';
import { Activity } from '@wallarm-org/design-system/icons';

export const CustomIcon = () => (
  <Avatar data-testid='avatar'>
    <AvatarFallback icon={Activity} />
  </Avatar>
);
```

### Branded

Inside a Branded frame the empty plate takes the brand tint and stroke, initials and the icon take the brand colour, and a photo is unchanged.

```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { HStack } from '@wallarm-org/design-system/Stack';

export const Branded = () => (
  <HStack gap={12} align='center' data-frame-style='branded' data-testid='avatar'>
    <Avatar>
      <AvatarFallback />
    </Avatar>
    <Avatar>
      <AvatarFallback name='Ada Lovelace' />
    </Avatar>
    <Avatar data-testid='avatar-branded-photo'>
      <AvatarImage src={PHOTO} />
      <AvatarFallback />
    </Avatar>
  </HStack>
);
```

### Click to upload

Click the avatar, or focus it and press Enter, to pick a photo — hover and focus show the edit
icon, and the pick only previews until your form saves.

```tsx
import { Avatar, AvatarFallback, AvatarImage, AvatarOverlay } from '@wallarm-org/design-system/Avatar';
import { FileUpload, FileUploadError, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';

export const ClickToUpload = () => {
  const { src, files, setFiles } = useAvatarUpload();
  return (
    <FileUpload
      data-testid='avatar-upload'
      accept={ACCEPT}
      maxFileSize={MAX_SIZE}
      value={files}
      onValueChange={setFiles}
    >
      <FileUploadTrigger asChild data-analytics-id='AVATAR_CHANGE'>
        <Avatar as='button' aria-label='Change avatar'>
          <AvatarImage src={src} />
          <AvatarFallback />
          <AvatarOverlay />
        </Avatar>
      </FileUploadTrigger>
      <FileUploadError />
    </FileUpload>
  );
};
```

### With actions

The settings-row pattern: explicit Remove and Replace buttons beside a plain avatar, with the accepted formats and size spelled out.

```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { Button } from '@wallarm-org/design-system/Button';
import { FieldDescription, FieldLabel } from '@wallarm-org/design-system/Field';
import { FileUpload, FileUploadError, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';
import { RefreshCcw, Trash2 } from '@wallarm-org/design-system/icons';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';

export const WithActions = () => {
  const { src, files, setFiles, remove } = useAvatarUpload(PHOTO);
  return (
    <FileUpload
      data-testid='avatar-upload'
      accept={ACCEPT}
      maxFileSize={MAX_SIZE}
      value={files}
      onValueChange={setFiles}
    >
      <HStack gap={12} align='center' className='w-[480px] justify-between'>
        <VStack gap={0} align='start'>
          <FieldLabel>Avatar</FieldLabel>
          <FieldDescription>PNG, JPEG, or WebP · up to 512 KB</FieldDescription>
        </VStack>
        <HStack gap={12} align='center'>
          {src && (
            <Button
              variant='ghost'
              color='neutral'
              size='small'
              aria-label='Remove avatar'
              data-testid='avatar-remove'
              onClick={remove}
            >
              <Trash2 />
            </Button>
          )}
          <FileUploadTrigger
            variant='ghost'
            color='neutral'
            size='small'
            aria-label='Replace avatar'
          >
            <RefreshCcw />
          </FileUploadTrigger>
          <Avatar data-testid='avatar'>
            <AvatarImage src={src} />
            <AvatarFallback />
          </Avatar>
        </HStack>
      </HStack>
      <FileUploadError />
    </FileUpload>
  );
};
```

### Uploading

While the new photo uploads, disable the trigger and force the overlay with `visible` so it holds a `Loader`.

```tsx
import { Avatar, AvatarFallback, AvatarImage, AvatarOverlay } from '@wallarm-org/design-system/Avatar';
import { FileUpload, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';
import { Loader } from '@wallarm-org/design-system/Loader';

export const Uploading = () => (
  <FileUpload data-testid='avatar-upload' accept={ACCEPT}>
    <FileUploadTrigger asChild disabled>
      <Avatar as='button' aria-label='Change avatar' aria-busy>
        <AvatarImage src={PHOTO} />
        <AvatarFallback />
        <AvatarOverlay visible>
          <Loader size='md' />
        </AvatarOverlay>
      </Avatar>
    </FileUploadTrigger>
  </FileUpload>
);
```

## Props

| Prop           | Type                                                    | Default | Description                                                                                     |
| -------------- | ------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------- |
| onStatusChange | `((details: StatusChangeDetails) => void) \| undefined` |         |                                                                                                 |
| defaultChecked | `boolean \| undefined`                                  |         |                                                                                                 |
| defaultValue   | `string \| number \| readonly string[] \| undefined`    |         |                                                                                                 |
| autoFocus      | `boolean \| undefined`                                  |         |                                                                                                 |
| id             | `string \| undefined`                                   |         |                                                                                                 |
| tabIndex       | `number \| undefined`                                   |         |                                                                                                 |
| title          | `string \| undefined`                                   |         |                                                                                                 |
| rel            | `string \| undefined`                                   |         |                                                                                                 |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`           |         |                                                                                                 |
| size           | `{ sm: string; md: string; lg: string; }`               | `lg`    |                                                                                                 |
| data-testid    | `string \| undefined`                                   |         |                                                                                                 |
| as             | `"span" \| undefined`                                   |         | Root tag. 'button' makes the avatar itself interactive (e.g. inside FileUploadTrigger asChild). |
| asChild        | `false \| undefined`                                    | `false` | Render the single child element as the root instead of `as`; takes precedence over it.          |
| type           | `"button" \| "submit" \| "reset" \| undefined`          |         |                                                                                                 |
| disabled       | `boolean \| undefined`                                  |         |                                                                                                 |
| name           | `string \| undefined`                                   |         |                                                                                                 |
| value          | `string \| number \| readonly string[] \| undefined`    |         |                                                                                                 |

### AvatarContent

| Prop           | Type                                                 | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined`                               |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |             |
| autoFocus      | `boolean \| undefined`                               |         |             |
| id             | `string \| undefined`                                |         |             |
| tabIndex       | `number \| undefined`                                |         |             |
| title          | `string \| undefined`                                |         |             |
| rel            | `string \| undefined`                                |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |             |

### AvatarDescription

| Prop           | Type                                                 | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined`                               |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |             |
| autoFocus      | `boolean \| undefined`                               |         |             |
| id             | `string \| undefined`                                |         |             |
| tabIndex       | `number \| undefined`                                |         |             |
| title          | `string \| undefined`                                |         |             |
| rel            | `string \| undefined`                                |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |             |

### AvatarFallback

| Prop           | Type                                                 | Default | Description                                                                                             |
| -------------- | ---------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
| name           | `string \| undefined`                                |         | Initials are taken from this: first letter of the first and of the last word (first word only at `sm`). |
| icon           | `ComponentType<SvgIconProps> \| undefined`           |         | Shown when there are no children and `name` gives no initials.                                          |
| defaultChecked | `boolean \| undefined`                               |         |                                                                                                         |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |                                                                                                         |
| autoFocus      | `boolean \| undefined`                               |         |                                                                                                         |
| id             | `string \| undefined`                                |         |                                                                                                         |
| tabIndex       | `number \| undefined`                                |         |                                                                                                         |
| title          | `string \| undefined`                                |         |                                                                                                         |
| rel            | `string \| undefined`                                |         |                                                                                                         |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |                                                                                                         |

### AvatarImage

| Prop           | Type                                                 | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title          | `string \| undefined`                                |         |             |
| defaultChecked | `boolean \| undefined`                               |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |             |
| autoFocus      | `boolean \| undefined`                               |         |             |
| tabIndex       | `number \| undefined`                                |         |             |
| rel            | `string \| undefined`                                |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |             |
| alt            | `string \| undefined`                                |         |             |
| height         | `string \| number \| undefined`                      |         |             |
| loading        | `"eager" \| "lazy" \| undefined`                     |         |             |
| src            | `string \| undefined`                                |         |             |
| width          | `string \| number \| undefined`                      |         |             |

### AvatarName

| Prop           | Type                                                 | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined`                               |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |             |
| autoFocus      | `boolean \| undefined`                               |         |             |
| id             | `string \| undefined`                                |         |             |
| tabIndex       | `number \| undefined`                                |         |             |
| title          | `string \| undefined`                                |         |             |
| rel            | `string \| undefined`                                |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |             |

### AvatarOverlay

| Prop           | Type                                                 | Default | Description                                                                      |
| -------------- | ---------------------------------------------------- | ------- | -------------------------------------------------------------------------------- |
| visible        | `boolean \| undefined`                               | `false` | Keep it shown regardless of hover — e.g. a `Loader` while the new photo uploads. |
| defaultChecked | `boolean \| undefined`                               |         |                                                                                  |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |                                                                                  |
| autoFocus      | `boolean \| undefined`                               |         |                                                                                  |
| id             | `string \| undefined`                                |         |                                                                                  |
| tabIndex       | `number \| undefined`                                |         |                                                                                  |
| title          | `string \| undefined`                                |         |                                                                                  |
| rel            | `string \| undefined`                                |         |                                                                                  |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |                                                                                  |
