> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AvatarGroup

> Overlapping avatars with a derived overflow indicator.

export const StoryEmbed = ({storyId, title, height = 120}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`AvatarGroup` receives rendered avatars and derives the hidden count. `maxVisible` counts actual avatars; the overflow indicator is additional. Eight supplied avatars with `maxVisible={3}` show three avatars and `+5`.

<StoryEmbed storyId="ui-data-display-avatargroup--display" title="Eight avatars with three visible" height={100} />

## Usage

```tsx theme={null}
import { AvatarGroup } from 'twenty-ui/components/data-display';
import { Avatar } from 'twenty-ui/primitives/data-display';

export const TeamAvatars = () => (
  <AvatarGroup
    maxVisible={3}
    overflowShape="circle"
    overlap="left"
    overlapOffset="4px"
    avatars={[
      'Ada',
      'Grace',
      'Margaret',
      'Annie',
      'Mary',
      'Dorothy',
      'Katherine',
      'Joan',
    ].map((name) => (
      <Avatar
        key={name}
        name={name}
        shape="circle"
        imageProps={{ alt: name }}
      />
    ))}
  />
);
```

Supply stable keys to keep surviving avatars mounted when the limit, total, or display order changes. Avatars are decorative by default. When the group is the only representation of its members, supply each member's name through `imageProps.alt` to label the image and default fallback. Keep avatars decorative when visible text or an owner label already supplies that identity.

## Visual options

Compare circle and square avatars at each size. The group keeps three visible members in this comparison.

<StoryEmbed storyId="ui-data-display-avatargroup--catalog" title="Compare avatar shapes and sizes" height={850} />

## Partially loaded collections

Use `total` for the full collection size when only some avatars are loaded. Three supplied and visible avatars with `total={20}` show `+17`. Pass all loaded avatars and let the group apply `maxVisible`.

```tsx theme={null}
import { AvatarGroup } from 'twenty-ui/components/data-display';
import { Avatar } from 'twenty-ui/primitives/data-display';

export const LoadedTeamAvatars = () => (
  <AvatarGroup
    maxVisible={3}
    total={20}
    avatars={['Ada', 'Grace', 'Margaret'].map((name) => (
      <Avatar key={name} name={name} imageProps={{ alt: name }} />
    ))}
  />
);
```

`total` is floored and cannot be smaller than the supplied renderable avatar count. Nonfinite totals use the supplied count. The derived hidden count is always nonnegative.

`maxVisible` defaults to `4`. Finite limits are floored and clamped to zero, `Infinity` shows all supplied avatars, and `NaN` shows none. Empty React nodes do not count as avatars. A zero limit can show only the overflow indicator. An empty group with a positive total can show that total in its indicator; an empty group with no hidden avatars renders nothing.

<StoryEmbed storyId="ui-data-display-avatargroup--partially-loaded" title="Three loaded members from a team of twenty" height={100} />

## Custom overflow

`renderOverflow` receives the same derived hidden count as the default `+N` indicator. It runs only when that count is positive. Return `null` to hide the indicator without changing which avatars are visible.

Use an explicit control when the indicator performs an action. Its accessible name should explain the action and the count.

```tsx theme={null}
import { AvatarGroup } from 'twenty-ui/components/data-display';
import { Avatar } from 'twenty-ui/primitives/data-display';
import { Button } from 'twenty-ui/primitives/input';

type TeamAvatarsWithActionProps = { onShowAll: () => void };

export const TeamAvatarsWithAction = ({
  onShowAll,
}: TeamAvatarsWithActionProps) => (
  <AvatarGroup
    maxVisible={3}
    total={20}
    avatars={['Ada', 'Grace', 'Margaret'].map((name) => (
      <Avatar key={name} name={name} imageProps={{ alt: name }} />
    ))}
    renderOverflow={(hiddenCount) => (
      <Button
        size="sm"
        aria-label={`Show ${hiddenCount} more members`}
        onClick={onShowAll}
      >
        +{hiddenCount}
      </Button>
    )}
  />
);
```

<StoryEmbed storyId="ui-data-display-avatargroup--custom-overflow" title="Custom overflow button" height={100} />

## Appearance

Later avatars always render above earlier ones. `overlap` picks the physical margin, `left` or `right`, that carries the negative offset. `overlapOffset` sets that offset as a CSS length. `overflowShape` sets the shape of the default numeric indicator.

## Root customization

Native attributes, event handlers, `className`, and `style` reach the root. Its default element is a `div`, including when `onClick` is supplied. Use native interactive elements for actions, such as the overflow button above.

The root accepts a ref and `render`, which can be an element or a function. Custom components must forward the supplied props and ref to their native root. See [custom components](/ui/primitives/overview#custom-components) for composition requirements.

```tsx theme={null}
import { useRef } from 'react';
import { AvatarGroup } from 'twenty-ui/components/data-display';
import { Avatar } from 'twenty-ui/primitives/data-display';

export const NamedTeamAvatars = () => {
  const groupRef = useRef<HTMLDivElement>(null);

  return (
    <AvatarGroup
      ref={groupRef}
      role="group"
      aria-label="Project members"
      style={{ marginBlock: 8 }}
      render={<div data-location="project-header" />}
      avatars={['Ada', 'Grace', 'Margaret'].map((name) => (
        <Avatar key={name} name={name} imageProps={{ alt: name }} />
      ))}
    />
  );
};
```

## Props

`AvatarGroupProps` is exported from `twenty-ui/components/data-display`.

<ParamField body="avatars" type="ReactNode[]" required>
  Avatar elements in display order. Empty React nodes are ignored. Supply stable keys to preserve identity when reordering.
</ParamField>

<ParamField body="className" type="string">
  Class applied to the group container.
</ParamField>

<ParamField body="maxVisible" type="number" default="4">
  Maximum number of actual avatars to show, excluding the overflow indicator. Floored and clamped to zero; Infinity shows all avatars and NaN shows none.
</ParamField>

<ParamField body="overflowShape" type="&#x22;circle&#x22; | &#x22;rounded-square&#x22; | &#x22;square&#x22;" default="square">
  Shape of the numeric overflow indicator.
</ParamField>

<ParamField body="overlap" type="&#x22;left&#x22; | &#x22;right&#x22;" default="right">
  Physical margin, left or right, that carries the negative overlap offset. Later avatars always render above earlier ones.
</ParamField>

<ParamField body="overlapOffset" type="string" default="3px">
  CSS length of the overlap between avatars.
</ParamField>

<ParamField body="ref" type="Ref<HTMLDivElement>">
  Ref to the rendered root element.
</ParamField>

<ParamField body="render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Element or render function for the root. Forward the supplied props and ref; the default element is a div.
</ParamField>

<ParamField body="renderOverflow" type="((hiddenCount: number) => ReactNode)">
  Called with the derived hidden count when it is positive. Return a custom indicator, or null to hide it.
</ParamField>

<ParamField body="total" type="number">
  Total collection size for partially loaded avatars. Floored and never smaller than the supplied renderable count; nonfinite values use the supplied count.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.