UsersAvatarGroup

free

UsersAvatarGroup turns person records into a compact participant stack with image, tooltip, overflow, and click handling.

Preview
Dark
import UsersAvatarGroup from '@/components/composites/UsersAvatarGroup';

const participants = [
  {
    name: 'Ava Chen',
    img: '/img/avatars/thumb-11.jpg',
  },
  {
    name: 'Daniel Kim',
    img: '/img/avatars/thumb-12.jpg',
  },
  {
    name: 'Mira Patel',
    img: '/img/avatars/thumb-13.jpg',
  },
  {
    name: 'Omar Ali',
    img: '/img/avatars/thumb-14.jpg',
  },
];

export default function UsageDemo() {
  return (
    <UsersAvatarGroup users={participants} />
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add UsersAvatarGroup

Examples

Overflow

Preview
Dark
+3
Select +3 to show all
import { useState } from 'react';
import Button from '@/components/ui/Button';
import UsersAvatarGroup from '@/components/composites/UsersAvatarGroup';

const participants = [
  { name: 'Ava Chen', img: '/img/avatars/thumb-11.jpg' },
  { name: 'Daniel Kim', img: '/img/avatars/thumb-12.jpg' },
  { name: 'Mira Patel', img: '/img/avatars/thumb-13.jpg' },
  { name: 'Omar Ali', img: '/img/avatars/thumb-14.jpg' },
  { name: 'Sora Ito', img: '/img/avatars/thumb-15.jpg' },
  { name: 'Theo Park', img: '/img/avatars/thumb-16.jpg' },
];

export default function OverflowDemo() {
  const [showAll, setShowAll] = useState(false);

  return (
    <div className="flex flex-col w-full max-w-md items-center gap-4">
      <UsersAvatarGroup
        users={participants}
        maxCount={showAll ? 0 : 3}
        onOmittedAvatarClick={() => setShowAll(true)}
      />
      {showAll ? (
        <Button
          variant="subtle"
          onClick={() => setShowAll(false)}
        >
          Show fewer
        </Button>
      ) : (
        <span>
          Select +3 to show all
        </span>
      )}
    </div>
  );
}

Custom Record Keys

imgKey and nameKey map the component to a record shape that uses different field names.

Preview
Dark
import UsersAvatarGroup from '@/components/composites/UsersAvatarGroup';

const reviewers = [
  {
    memberId: '001',
    displayName: 'Cora Singh',
    photo: '/img/avatars/thumb-17.jpg',
  },
  {
    memberId: '002',
    displayName: 'Ethan Tan',
    photo: '/img/avatars/thumb-18.jpg',
  },
  {
    memberId: '003',
    displayName: 'Ivy Lim',
    photo: '/img/avatars/thumb-19.jpg',
  },
];

export default function CustomRecordKeysDemo() {
  return (
    <UsersAvatarGroup
      users={reviewers}
      imgKey="photo"
      nameKey="displayName"
    />
  );
}

Avatar And Group Props

avatarProps applies one Avatar configuration to every participant, while direct Avatar.Group props control the stack.

Preview
Dark
import UsersAvatarGroup from '@/components/composites/UsersAvatarGroup';

const participants = [
  { name: 'Ava Chen', img: '/img/avatars/thumb-11.jpg' },
  { name: 'Daniel Kim', img: '/img/avatars/thumb-12.jpg' },
  { name: 'Mira Patel', img: '/img/avatars/thumb-13.jpg' },
];

export default function AvatarAndGroupPropsDemo() {
  return (
    <UsersAvatarGroup
      users={participants}
      chained={false}
      avatarProps={{
        size: 40,
      }}
    />
  );
}

Avatar Click

onAvatarClick receives the selected user record when a participant avatar is clicked.

Preview
Dark

Selected:Ava Chen

import { useState } from 'react';
import UsersAvatarGroup from '@/components/composites/UsersAvatarGroup';

const participants = [
  { name: 'Ava Chen', img: '/img/avatars/thumb-11.jpg' },
  { name: 'Daniel Kim', img: '/img/avatars/thumb-12.jpg' },
  { name: 'Mira Patel', img: '/img/avatars/thumb-13.jpg' },
];

export default function AvatarClickDemo() {
  const [selected, setSelected] = useState(participants[0]);

  return (
    <div className="flex w-full max-w-md flex-col items-center gap-2">
      <UsersAvatarGroup
        users={participants}
        onAvatarClick={(user) => {
          setSelected({
            name: user.name,
            img: user.img,
          });
        }}
      />
      <p role="status">
        <span className="text-muted-foreground">
          Selected: 
        </span>
        <span className="font-medium">
          {selected.name}
        </span>
      </p>
    </div>
  );
}

API

UsersAvatarGroup is a record-to-avatar adapter over Avatar.Group. It uses the configured image field for src, the configured name field for each tooltip, and forwards the remaining Avatar.Group props.

PropDescriptionTypeDefault
usersString-keyed user records rendered as avatars.UserRecord[][]
imgKeyUser-record field passed to the Avatar src prop.string'img'
nameKeyUser-record field shown in each Avatar tooltip.string'name'
avatarPropsProps applied to every generated Avatar after the wrapper defaults.AvatarProps{}
avatarGroupPropsAvatar.Group props applied before direct group props.AvatarGroupProps{}
onAvatarClickCalled with the clicked user record.UserAvatarClickHandler-
chainedOverlaps the avatars into a stack.booleantrue
maxCountVisible-avatar limit before the generated +N avatar appears.number4
...AvatarGroupPropsDirect Avatar.Group props, such as className, omittedAvatarContent, and onOmittedAvatarClick. These take priority over the same field in avatarGroupProps.AvatarGroupProps-

Each generated Avatar defaults to shape="circle", size={30}, and a pointer cursor. avatarProps can override those defaults.

Types

import type {
  AvatarGroupProps,
  AvatarProps,
} from '@/components/ui/Avatar';
 
type UserRecord = Record<string, string>;
 
type UserAvatarClickHandler = (user: UserRecord) => void;
 
type UsersAvatarGroupProps = AvatarGroupProps & {
  avatarGroupProps?: AvatarGroupProps;
  avatarProps?: AvatarProps;
  imgKey?: string;
  nameKey?: string;
  onAvatarClick?: UserAvatarClickHandler;
  users?: UserRecord[];
};