Multi Select
Lets the user pick several options from a searchable list opened by a button, with the chosen options shown as independently removable chips beside a native opener button.
Installation
npx shadcn@latest add @redpanda/multi-selectpnpm dlx shadcn@latest add @redpanda/multi-selectyarn dlx shadcn@latest add @redpanda/multi-selectbunx shadcn@latest add @redpanda/multi-selectnubx shadcn@latest add @redpanda/multi-selectaube dlx shadcn@latest add @redpanda/multi-selectTweaked by malinskibeniamin, multi select component combines regular select, badge, command and tooltip. It lets users see and select multiple items.
When to use
The Multi Select component allows users to select multiple options from a list. Use this decision tree to determine when it’s appropriate:
Usage
import { MultiSelect, MultiSelectContent, MultiSelectControl, MultiSelectItem, MultiSelectList, MultiSelectTrigger, MultiSelectValue,} from '@/components/redpanda-ui/multi-select';<MultiSelect> <label htmlFor="framework-choice">Frameworks</label> <MultiSelectControl className="w-80"> <MultiSelectValue /> <MultiSelectTrigger id="framework-choice" placeholder="Select frameworks" /> </MultiSelectControl> <MultiSelectContent> <MultiSelectList> <MultiSelectItem value="react">React</MultiSelectItem> <MultiSelectItem value="vue">Vue</MultiSelectItem> <MultiSelectItem value="angular">Angular</MultiSelectItem> <MultiSelectItem value="svelte">Svelte</MultiSelectItem> </MultiSelectList> </MultiSelectContent></MultiSelect>Control anatomy and keyboard behavior
MultiSelectControl is the noninteractive field shell and popup anchor. Its selected values and opener are siblings:
MultiSelect├─ MultiSelectControl│ ├─ MultiSelectValue → selected chips, each with a removal button│ └─ MultiSelectTrigger → native button that opens the options└─ MultiSelectContent → search and option listKeep MultiSelectValue outside MultiSelectTrigger. Do not nest interactive content inside the opener. Give the opener a persistent name using an associated <label htmlFor> and id, aria-labelledby, or aria-label. Forward form IDs, descriptions, invalid state, blur handlers, and button refs to MultiSelectTrigger, not the shell. Place FormControl around the trigger only.
- Enter or Space on the opener opens the popup. Keyboard opening focuses the search input when present; Escape closes it and returns focus to the opener.
- Each selected chip has a named
Remove …button. Removing a chip does not open or close the popup. - After an accepted removal, focus moves to the next visible removal action, then the previous one, or the opener when no other visible action remains. Ignored or failed controlled updates do not move focus away from the original action. Synchronous removal callback failures display an error.
disabledonMultiSelectdisables the opener, removal actions, search, and options. The opener and removal actions usetype="button"; they do not submit enclosing forms.maxDisplaylimits visible chips; all selections remain in the value.maxItemLengthtruncates visible text but keeps the full removal name and exposes the full label in a tooltip on the removal action.
Migrating the compound API
Replace the former nested trigger/value structure with the sibling anatomy above. Move field width/layout classes from MultiSelectTrigger to MultiSelectControl, and move placeholder from MultiSelectValue to MultiSelectTrigger.
MultiSelectTrigger now forwards native button attributes/events and an HTMLButtonElement ref instead of rendering a role-button div. It no longer accepts children; its native type="button" is not configurable. Its content and type="button" are owned by the component; there is no render, asChild, or nativeButton={false} escape. MultiSelectValue no longer accepts placeholder. SimpleMultiSelect already builds the new anatomy; existing value/options/width contracts remain available.
Examples
Default
Simple
Form
Controllable Search
search by control or load remote data
Input Custom (tag mode)
Allow user to select from input custom tag
Limit Count
limit max select count by maxCount
Limit item display length
limit max display item count with maxDisplay, or limit item display length with maxItemLength