# Avatar Group

Show several people as a row of overlapping avatars with an optional overflow count.

Project members with overflow count

[Complete TSX file](/examples/avatar-group/default.tsx)

```tsx
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/siteplane/components/avatar";
import {
  AvatarGroup,
  AvatarGroupCount,
} from "@/siteplane/components/avatar-group";

const members = [
  {
    name: "Mara Ellis",
    initials: "ME",
    image: "https://ui.siteplane.io/avatars/mara.webp",
  },
  {
    name: "Ben Carter",
    initials: "BC",
    image: "https://ui.siteplane.io/avatars/ben.webp",
  },
  {
    name: "Sofia Nguyen",
    initials: "SN",
    image: "https://ui.siteplane.io/avatars/sofia.webp",
  },
];

export default function Example() {
  return (
    <AvatarGroup>
      {members.map((member) => (
        <Avatar key={member.name}>
          <AvatarImage alt={member.name} src={member.image} />
          <AvatarFallback>{member.initials}</AvatarFallback>
        </Avatar>
      ))}
      <AvatarGroupCount>
        +2<span className="sr-only"> more members</span>
      </AvatarGroupCount>
    </AvatarGroup>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/avatar-group
```

With npm: `npx shadcn@4.18.0 add @siteplane/avatar-group`. [Registry JSON](/registry/avatar-group.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/avatar @siteplane/card
```

## Usage

```tsx
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/siteplane/components/avatar";
import {
  AvatarGroup,
  AvatarGroupCount,
} from "@/siteplane/components/avatar-group";

<AvatarGroup>
  <Avatar>
    <AvatarImage src="/avatars/mara-ellis.png" alt="Mara Ellis" />
    <AvatarFallback>ME</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarImage src="/avatars/ben-carter.png" alt="Ben Carter" />
    <AvatarFallback>BC</AvatarFallback>
  </Avatar>
  <AvatarGroupCount>
    +2<span className="sr-only"> more members</span>
  </AvatarGroupCount>
</AvatarGroup>;
```

## Composition

```text
AvatarGroup
├── Avatar (one per person)
└── AvatarGroupCount (optional)
```

Put the avatars directly in the group, followed by an `AvatarGroupCount` for the people it does not show. Each item after the first overlaps the one before it by a quarter of its size, and later items sit on top of earlier ones. Every avatar and the count draw a 2 px ring in the color of the surface they sit on, so the circles stay apart on the page, on a Card or in a Dialog.

## Sizes

Set `size` on the group, not on each avatar: `sm` 24 px, `md` 32 px (the default, the size of a single Avatar), `lg` 40 px or `xl` 48 px. The group sizes every avatar and the count, and the overlap scales with the size.

Avatar groups in four sizes

[Complete TSX file](/examples/avatar-group/sizes.tsx)

```tsx
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/siteplane/components/avatar";
import {
  AvatarGroup,
  AvatarGroupCount,
} from "@/siteplane/components/avatar-group";

const sizes = ["sm", "md", "lg", "xl"] as const;

const members = [
  {
    name: "Mara Ellis",
    initials: "ME",
    image: "https://ui.siteplane.io/avatars/mara.webp",
  },
  {
    name: "Ben Carter",
    initials: "BC",
    image: "https://ui.siteplane.io/avatars/ben.webp",
  },
  {
    name: "Sofia Nguyen",
    initials: "SN",
    image: "https://ui.siteplane.io/avatars/sofia.webp",
  },
];

export default function Example() {
  return (
    <div className="flex flex-col items-center gap-4">
      {sizes.map((size) => (
        <AvatarGroup key={size} size={size}>
          {members.map((member) => (
            <Avatar key={member.name}>
              <AvatarImage alt={member.name} src={member.image} />
              <AvatarFallback>{member.initials}</AvatarFallback>
            </Avatar>
          ))}
          <AvatarGroupCount>
            +9<span className="sr-only"> more members</span>
          </AvatarGroupCount>
        </AvatarGroup>
      ))}
    </div>
  );
}
```

## Large counts

Pass the number of people the group does not show as `count`. The count writes it as `+12`, and from 1,000 on in compact notation, such as `+1.3K`, with the exact number as its title; assistive technology reads the exact number, followed by the children, such as visually hidden text that says what it counts. A count never truncates: a longer one grows as a pill of the group's height, and a short one stays a circle. `locale` sets the locale that writes the number, `en-US` unless set, so the server and the browser render the same text.

A small and a large overflow count

[Complete TSX file](/examples/avatar-group/count.tsx)

```tsx
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/siteplane/components/avatar";
import {
  AvatarGroup,
  AvatarGroupCount,
} from "@/siteplane/components/avatar-group";

const members = [
  {
    name: "Mara Ellis",
    initials: "ME",
    image: "https://ui.siteplane.io/avatars/mara.webp",
  },
  {
    name: "Ben Carter",
    initials: "BC",
    image: "https://ui.siteplane.io/avatars/ben.webp",
  },
  {
    name: "Sofia Nguyen",
    initials: "SN",
    image: "https://ui.siteplane.io/avatars/sofia.webp",
  },
];

export default function Example() {
  return (
    <div className="grid gap-4">
      {[12, 1284].map((count) => (
        <AvatarGroup key={count}>
          {members.map((member) => (
            <Avatar key={member.name}>
              <AvatarImage alt={member.name} src={member.image} />
              <AvatarFallback>{member.initials}</AvatarFallback>
            </Avatar>
          ))}
          <AvatarGroupCount count={count}>
            <span className="sr-only"> more members</span>
          </AvatarGroupCount>
        </AvatarGroup>
      ))}
    </div>
  );
}
```

## On a card

The ring takes the color of the Card, so the group needs no border of its own. A small group fits in the `CardAction` beside a title, for example the reviewers of a task.

Reviewers of a design review on a card

[Complete TSX file](/examples/avatar-group/card.tsx)

```tsx
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/siteplane/components/avatar";
import {
  AvatarGroup,
  AvatarGroupCount,
} from "@/siteplane/components/avatar-group";
import {
  Card,
  CardAction,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/siteplane/components/card";

const reviewers = [
  {
    name: "Lea Hartmann",
    initials: "LH",
    image: "https://ui.siteplane.io/avatars/lea.webp",
  },
  {
    name: "Jonas Weber",
    initials: "JW",
    image: "https://ui.siteplane.io/avatars/jonas.webp",
  },
  {
    name: "Anna Brooks",
    initials: "AB",
    image: "https://ui.siteplane.io/avatars/anna.webp",
  },
];

export default function Example() {
  return (
    <Card className="w-full max-w-md">
      <CardHeader>
        <CardTitle>Design review</CardTitle>
        <CardDescription>Due Friday</CardDescription>
        <CardAction>
          <AvatarGroup size="sm">
            {reviewers.map((reviewer) => (
              <Avatar key={reviewer.name}>
                <AvatarImage alt={reviewer.name} src={reviewer.image} />
                <AvatarFallback>{reviewer.initials}</AvatarFallback>
              </Avatar>
            ))}
            <AvatarGroupCount>
              +4<span className="sr-only"> more reviewers</span>
            </AvatarGroupCount>
          </AvatarGroup>
        </CardAction>
      </CardHeader>
    </Card>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| AvatarGroup.size | sm \| md \| lg \| xl | md | Size of every avatar and the count: 24, 32, 40 or 48 px. The overlap is a quarter of it. |
| AvatarGroup.render | ReactElement \| render function | div | Composes the group onto another element. |
| AvatarGroup.className | string | unset | Classes for the row. |
| AvatarGroupCount.count | number | unset | The number of people the group does not show, written as +N and from 1,000 on in compact notation (+1.3K), with the exact number for assistive technology and as the title. |
| AvatarGroupCount.locale | string | en-US | The locale that writes count. |
| AvatarGroupCount.children | ReactNode | unset | The count as text, such as +2, or the words after count; give the count an accessible name that says what it counts. |
| AvatarGroupCount.render | ReactElement \| render function | span | Composes the count onto another element, such as a link to the full list. |

`AvatarGroup` renders a div with `data-slot="avatar-group"` and accepts native div props and refs. `AvatarGroupCount` renders a span with `data-slot="avatar-group-count"` and accepts the props of [Badge](/docs/components/badge/) except `variant`, `size`, `shape`, `profile` and `light`, which the group sets. With `count`, the visible figure is a span with `data-slot="avatar-group-count-value"`. The module exports `AvatarGroupProps`, `AvatarGroupSize` and `AvatarGroupCountProps`.

[Read the installed source](/source/avatar-group).

## Usage notes

The application decides how many avatars to show and what the count says. Give each avatar a name: an image needs a meaningful `alt`, and an avatar that shows only initials can take `role="img"` and an `aria-label`. Give the count an accessible name that says what it counts, such as visually hidden text after the number.
