shadcn.io is not affiliated with official shadcn/ui
Contact Picker Dialog Block for React
A compose window with its recipient picker shown open: a search over fourteen contacts, four recent ones as toggles, an alphabetical list with pinned letters, a To panel of removable chips, and an Add recipients button that fills the message's To line.
Installation
Overview
Choose who a message goes to without leaving it, with this recipient picker for React with Base UI. It opens from a message being written in Postbag, an email client: the page's header holds an outline Choose recipients Button, and the compose window itself, a title bar, a To line, a subject and a body, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog is a split. At the start are a search Input that narrows fourteen contacts by name and address, four recent contacts as toggle chips, and the whole address book in alphabetical groups with each letter pinned as you scroll, every row a checkbox with initials, a name and an address. At the end a "To (3)" panel lists the chosen people as chips you can remove, with a Clear button. Add 3 recipients, the only Shine Button, counts as you choose and puts the chips on the drawn To line. For preview the dialog is drawn open in place when the page loads; the Choose recipients button opens the real Dialog, with the same choice. For mail, sharing and message tools, and anywhere a person is picked from a book.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the compose window: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Choose recipients button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same PickerFields component and share the block's state, so what you chose in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
How does the search work?
It folds case and accents on both sides, so "elif" and "Élif" find the same row, and it matches the name or the address. Groups with no match disappear and the pinned letters follow. When nothing matches, the list shows an empty state that names what you typed and has a Clear search button. The box keeps its height, so the chosen panel beside it never moves. A polite count ("2 contacts match, 3 chosen.") is said 600 ms after you stop typing, never on every key. In your app call your own search and keep the same rows.
How do I choose and remove people?
Each contact is a native checkbox inside a label, so Space toggles the focused row and pressing anywhere on it works too. The four recent chips toggle the same people, and are pressed when they are chosen. The To panel lists the chosen people in the order you picked them, each with a Remove button named for the person, and Clear takes them all off. After a removal focus moves to the next Remove button, or to the search when none is left. Add recipients with nobody chosen says so under the lists and puts focus in the search.
What stays local in the demo, and how do I wire it up?
All of it. The fourteen contacts and the four recent ones are typed constants, and Add recipients only puts the chosen people on the drawn To line and says in a polite message that nothing was sent. Cancel and Escape put the choice back to what the message already has. In your app load the book from your API, keep the choice in picked, and on add write the addresses into your message's To field.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus starts in the search and stays inside until it closes, and Escape, Cancel and the close button return focus to Choose recipients. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on the button.
How does it look on a phone?
The copy shown on load docks to the foot of the page as a bottom sheet, with Add recipients pinned under the body. The To panel becomes a single row of chips under the list that scrolls sideways, so the list keeps its height. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.
Related
- Pro
Invite Members Dialog
Share and inviteA household calendar's Members page with its invite dialog shown open: one email field that knows the household's domain, so an outside address joins as a guest, the people who already have access, and a Send invite button that adds a pending row.
- Pro
Invite Team Dialog
Share and inviteA crew page with its bulk invite dialog shown open: a pasted list becomes one chip per address with bad ones flagged, one role for the batch, a seat meter that refuses a batch that does not fit, and a Pending tab with Resend, Revoke and Undo.
- Pro
Contact Card Dialog
Share and inviteA team directory with a contact card shown open that pages through six people: initials avatar, role, email, phone, desk and the time where they are, copy buttons, real mailto and tel links, and a Save contact button that downloads a vCard.
- Pro
Email Client Sidebar
App contentA mail client in a fixed-height window: mailboxes and labels whose counts follow the mail you file, a searchable message list beside the open message, and Compose for a local draft.
- Pro
A mail navbar for an email app with a search field that turns from:, to:, has: and is: operators into removable chips and counts the matches.
- Pro
Address Autocomplete Dialog
Pickers and searchA meal-kit delivery page with its address dialog shown open: typing filters eight addresses of an invented town into an inline list you can arrow through, a choice fills four editable fields, and Enter it manually offers the same four, empty.