Components
MultiSelect
Headless compact multi-value selection primitives.
When to use
Use MultiSelect when users choose several values from a predefined moderate collection and the options should stay collapsed until requested. Use CheckboxGroup for a short visible choice set, Listbox when the collection should remain visible, Select for one value, and Combobox when editable filtering is required.
Import
import { MultiSelect } from "@flowstack-ui/atom/multi-select";
Anatomy
<MultiSelect.Root defaultValue={["design"]} name="skills">
<MultiSelect.Trigger>
<MultiSelect.Value placeholder="Choose skills" />
<MultiSelect.Icon />
</MultiSelect.Trigger>
<MultiSelect.Content>
<MultiSelect.ScrollUpButton />
<MultiSelect.Viewport>
<MultiSelect.Group>
<MultiSelect.Label>Skills</MultiSelect.Label>
<MultiSelect.Item value="design">
<MultiSelect.ItemText>Design</MultiSelect.ItemText>
<MultiSelect.ItemIndicator />
</MultiSelect.Item>
</MultiSelect.Group>
</MultiSelect.Viewport>
<MultiSelect.ScrollDownButton />
<MultiSelect.Arrow />
</MultiSelect.Content>
</MultiSelect.Root>
API Reference
Root
| Prop | Type | Default |
|---|---|---|
children | ReactNode | required |
value | string[] | - |
defaultValue | string[] | [] |
onValueChange | (value: string[]) => void | - |
open / defaultOpen | boolean | false |
onOpenChange | (open: boolean) => void | - |
disabled / readOnly / invalid / required | boolean | Field value / false |
name / form | string | - |
validationBehavior | "native" | "inline" | Field/Form value |
Values are deduplicated in first-occurrence order. Item activation toggles one value without closing Content. Uncontrolled value and open state return to their defaults on form reset.
Parts
Triggerrenders a button and ownsaria-expanded,aria-haspopup,aria-controls, Field label/description relationships, state data, native props, ref,asChild, andrender.Valuerenders the placeholder for zero values, one label for one value, orFirst (+N more)for several.renderValue(values, labels)customizes the summary without changing behavior.Contentand itsListboxalias render the positioned popup, portal by default, and exposedata-side,data-align, anddata-positioned.Viewport, scroll buttons, andArrowown the specialized overflow and positioned-popup anatomy.Itemrequiresvalue, acceptslabelanddisabled, exposes option ARIA, checked/highlighted/disabled data, and stays open after toggling.ItemText,ItemIndicator,Group,Label, andSeparatorpreserve the complete collection anatomy.Portalacceptscontaineranddisabled.
Accessibility and keyboard
Trigger is a button that opens a focusable popup listbox; it is deliberately
not role="combobox" because the APG combobox value model is single-select.
Content has role="listbox" and aria-multiselectable="true"; Items have
role="option" and aria-selected.
- Arrow keys move the active option without changing selection.
- Home/End move to the first/last enabled option.
- Space or Enter toggles the active option and keeps Content open.
- Printable keys use option labels for typeahead.
- Escape closes and restores Trigger focus.
- Tab and touch-safe outside interaction close the popup.
Forms and Field
When named or required, Root renders an accessibility-hidden native multiple select containing every declared Item. It carries the selected values, name, form, disabled, and required state; redirects invalid focus to Trigger; and uses Atom's Field/Form validation and reset infrastructure. Item labels are collected statically so closed values and native options work before the popup mounts.
Data Attributes
- Trigger and Content expose
data-state="open|closed"; Content also exposes resolveddata-side,data-align, anddata-positioned. - Item exposes
data-state="checked|unchecked",data-value, and optionaldata-highlightedanddata-disabled. - Stateful parts expose
data-disabled,data-readonly, anddata-invalidwhere those states apply. Every rendered part exposes an overridabledata-slot.
Scope
MultiSelect intentionally excludes editable filtering, arbitrary tags, creation, select-all, range selection, virtualization, async loading, and mandatory chips. Those require separate interaction contracts.
Evidence
test/primitives/multi-select.test.mjsplayground/manual-tests/multi-select.md- playground
multi-selectscenario andMultiSelectworkbook sheet
Changelog
0.10.1
- Move required and read-only ARIA states from the native button trigger to the multiple-selection listbox that supports them.
0.10.0
- Add the complete compact multi-value selection primitive with button-owned popup listbox semantics, array state, summary rendering, positioning, dismissal, Field/Form participation, validation, reset, and full anatomy.