Skip to content

MultiSelect

Multi-value picker with a fixed-height segmented trigger; selected values render as removable chips that collapse to a +N badge, and the dropdown lists every option with a search field.

  • stable
  • since v0.1.0
  • 3.06 kB
  • forms
  • input

role="combobox" trigger with an aria-multiselectable listbox; options stay in the list marked selected; Backspace in the empty search removes the last chip.

Rendered as a segmented control — chips sit in a raised value tile and the chevron in its own slot inside the tinted track, matching the other inputs. The trigger keeps a fixed height: chips that don’t fit collapse into a +N badge instead of wrapping. Opening it reveals a search field — the Cynosure SearchInput, pinned to the top while the list scrolls — and the full option list; every item stays in the list (marked with a checkmark) and toggles on click, so nothing becomes unreachable.

Preview
Open
tsx

Three sizes are available: sm, md (default), and lg.

Preview
Open
tsx

Set maxSelected to cap how many values can be added. Items past the cap render as disabled in the listbox.

Preview
Open
tsx
Preview
Open
tsx

Set invalid to mark the field for validation feedback.

Preview
Open
tsx
PropTypeDefaultDescription
value
readonly T[]
Controlled array of selected values.
defaultValue
readonly T[]
Uncontrolled initial array of selected values.
onValueChange
((value: T[]) => void)
Fires with the next array on every add/remove.
items*
readonly MultiSelectItemData<T>[]
Options to choose from.
label
string
Visible label, also used as the trigger's accessible name.
aria-label
string
Aria label when no visual label is available.
placeholder
string
"Select…"
Placeholder rendered when nothing is selected.
searchPlaceholder
string
"Search…"
Placeholder for the in-dropdown search field.
size
"sm"|"md"|"lg"
"md"
Control size.
variant
"outline"|"filled"|"ghost"
"outline"
Visual treatment.
disabled
boolean
Disables interaction.
required
boolean
Requires at least one selection for form submission.
invalid
boolean
Renders the invalid state and sets `aria-invalid`.
maxSelected
number
Hard cap on how many items can be selected.
emptyState
ReactNode
Rendered when the filtered dropdown is empty.
className
string
style
CSSProperties
name
string
id
string
ref
Ref<HTMLDivElement>
  • The popup uses role="listbox" with aria-multiselectable="true" and role="option" items.
  • The internal <input> keeps focus while typing filters the listbox.
  • Backspace on an empty input removes the most recently selected value.
  • ArrowDown opens the popover; Escape closes it without losing focus.
  • Each tag’s remove button has an accessible label of the form Remove {label}.
  • Provide an aria-label (or label) when the field has no visible caption.
  • Use emptyState to render a custom node when the filtered list is empty.
  • Set maxSelected for tag-limited filters; the listbox surfaces the cap via aria-disabled.