An avatar cluster that opens a searchable list of people — tick several to assign them, in a table cell or a card.
npx shadcn@latest add https://sdk-components.thesqd.com/r/avatar-picker.jsonimport { AvatarPicker } from "@/components/blocks/avatar-picker";import * as React from "react";
import { AvatarPicker } from "@/components/blocks/avatar-picker";
const people = [
{ id: "blake", name: "Blake Riley", src: "/images/avatar/blake-riley.jpg", description: "blake@thesqd.com" },
{ id: "benedict", name: "Benedict Doherty", src: "/images/avatar/benedict-doherty.jpg", description: "benedict@thesqd.com" },
{ id: "alisa", name: "Alisa Hester", src: "/images/avatar/alisa-hester.jpg", description: "alisa@thesqd.com" },
{ id: "jordan", name: "Jordan Lee", description: "jordan@thesqd.com" },
{ id: "sam", name: "Sam Carter", description: "sam@thesqd.com" },
];
export function TaskAssignees() {
const [picked, setPicked] = React.useState<string[]>(["blake", "alisa"]);
return (
<AvatarPicker
people={people}
value={picked}
onValueChange={setPicked}
suggested={["jordan", "benedict"]}
/>
);
}
AvatarPicker (Combobox, multiple)
├── trigger (Avatar people group, max + "+N" chip;
│ a dashed UserPlus circle when nobody is picked)
└── ComboboxContent
├── Input search (the Inputs page's search field; searches name and description)
├── ComboboxEmpty
└── ComboboxList
└── ComboboxGroup × 3 (Selected, Suggested, Everyone — fixed when the list opens)
└── ComboboxItem (Avatar xs, name over description, check when picked)AvatarPicker
| Prop | Type | Default | Description |
|---|---|---|---|
people | AvatarPickerPerson[] | — | Everyone who can be picked. |
value | string[] | — | Picked ids, in order (controlled). |
defaultValue | string[] | — | Initially picked ids (uncontrolled). |
onValueChange | (value: string[]) => void | — | Fires with the whole new list on every pick or un-pick. |
single | boolean | — | One person at most: a pick replaces the owner and closes the list; picking them again clears it. `value` is still an array (zero or one id). |
suggested | string[] | — | Ids for a "Suggested" section between the picked people and everyone else, best first. Picked ids are skipped. |
onOpenChange | (open: boolean) => void | — | Fires when the list opens or closes — fetch `suggested` here. |
sectionLabels | { selected?; suggested?; everyone? } | "Selected" / "Suggested" / "Everyone" | Section headings. |
max | number | 3 | Faces shown before the `+N` chip. |
size | AvatarSize | "xs" | Size of the trigger's faces. |
label | string | "Assign people" | Accessible name for the trigger. |
searchPlaceholder | string | "Search people..." | Placeholder for the search field. |
emptyMessage | ReactNode | "No one found." | Shown when the search matches nobody. |
disabled | boolean | — | Disable the trigger. |
className | string | — | Extra classes on the trigger button. |
AvatarPickerPerson
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Stable key (email, user id). What `value` holds. |
name | string | — | Name in the list; drives the initials fallback. |
src | string | null | — | Photo URL. |
description | string | — | Muted second line in the list, also searched. |