Assign or mention people and teams: task assignee, approvers, deal owner, reviewers, shift staff, share-with. A Record picker for people — avatars with presence, role line, “Assign to me”, deactivated users, teams that expand to their members, and an avatar-stack trigger for table cells.
Deactivated people stay listed but can't be picked.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { Field, UserPicker } from "@vamos-ui/react";
<Field label="Assignee">
<UserPicker source={people} currentUserId={me.id} value={assigneeId} onValueChange={setAssigneeId} />
</Field>For @mentions in comments, reuse PickerUser and the row look; the mention menu itself belongs to the Comment thread.
Examples
Avatar stack in a table
trigger="avatars" shows the people as a stack plus “+”. The button is named “Assignees of T-482: Priya Raman, Tom Hollis”.
| Task | Due | Assignees |
|---|---|---|
| T-482Migrate pricing page to new CMS blocks | Nov 14 | |
| T-487Q4 renewal emails for Growth plan | Nov 18 | |
| T-491Fix SLA timer on reopened tickets | Nov 21 |
People and teams
teams adds a Teams group. With getTeamMembers a team expands to its active members; without it the team id (team:<id>) is selected.
Pick a team to add all of its members.
Assign to me
currentUserId pins an “Assign to me” row and marks you with “(you)”.
Invoice INV-2042 was charged twice
Inline list (shift staff)
trigger="inline" keeps the list open in a side panel. max limits the positions.
Saturday, 07:00 – 15:00
1 of 3 positions filled
No results
No resultsAPI
UserPicker
| Prop | Type | Default |
|---|---|---|
sourcePeople. An array is searched in memory (name, email, role; locale-folded) and sorted by name with the locale collator. | PickerUser[] | RecordSource<PickerUser> | — |
multiplePick many (value becomes string[]). | boolean | false |
value / defaultValue / onValueChangeControlled or uncontrolled id(s). | as Record picker | — |
maxMulti: most people. | number | — |
triggerField box, avatar-stack button or an always-open list. | "field" | "avatars" | "inline" | "field" |
currentUserIdAdds “Assign to me” and the “(you)” suffix. | string | — |
teamsTeams in their own group ({ id, name, memberCount }). | PickerTeam[] | RecordSource<PickerTeam> | — |
getTeamMembersResolve a picked team to people (deactivated members are skipped). | (team, signal) => Promise<PickerUser[]> | — |
maxAvatarsAvatars before “+N” in the avatar trigger. | number | 3 |
roleLabelRole in the avatar trigger's accessible name. | string | "Assignees" |
selectedRecordsPeople for the initial value with a server source. | PickerUser[] | — |
sizeTrigger size. | "sm" | "md" | "lg" | "md" |
placeholderTrigger text when empty. | string | "Unassigned" / "Assign" |
name / disabled / readOnly / invalid / requiredAs Record picker; also read from Field. | … | — |
labelsBuilt-in strings (namespace userPicker). | Partial<UserPickerLabels> | — |
PickerUser
| Prop | Type | Default |
|---|---|---|
id / nameIdentity and display name (initials come from it, locale-aware). | string | — |
emailSearchable; shown when there is no meta. | string | — |
avatarUrlPhoto; falls back to initials. | string | null | — |
statusPresence dot. | "online" | "away" | "busy" | "offline" | — |
metaRole or department line. | string | — |
disabledDeactivated: dimmed and not selectable, still shown when already assigned. | boolean | — |