UsersAvatarGroup
freeUsersAvatarGroup turns person records into a compact participant stack with image, tooltip, overflow, and click handling.
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 UsersAvatarGroupExamples
Overflow
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.
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.
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.
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.
| Prop | Description | Type | Default |
|---|---|---|---|
users | String-keyed user records rendered as avatars. | UserRecord[] | [] |
imgKey | User-record field passed to the Avatar src prop. | string | 'img' |
nameKey | User-record field shown in each Avatar tooltip. | string | 'name' |
avatarProps | Props applied to every generated Avatar after the wrapper defaults. | AvatarProps | {} |
avatarGroupProps | Avatar.Group props applied before direct group props. | AvatarGroupProps | {} |
onAvatarClick | Called with the clicked user record. | UserAvatarClickHandler | - |
chained | Overlaps the avatars into a stack. | boolean | true |
maxCount | Visible-avatar limit before the generated +N avatar appears. | number | 4 |
...AvatarGroupProps | Direct 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[];
};