MultiSelect
import { MultiSelect, useMultiSelect } from '@tangible/ui';
Interactive docs and examples: MultiSelect in Storybook.
MultiSelect props
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
||
aria-describedby? |
string |
ID of element(s) that describe this select. | |
aria-invalid? |
"false" | "true" | boolean |
Marks the control invalid. Lands on the trigger, which also wears tui-input-group — that is what lights the danger border. Injected automatically by Field.Control when the Field has an error. |
|
aria-label? |
string |
Accessible name for the select. | |
aria-labelledby? |
string |
ID of element that labels this select. | |
aria-required? |
"false" | "true" | boolean |
Marks the control required. Injected automatically by Field.Control when the Field is required. |
|
defaultOpen? |
boolean |
false |
Default open state for uncontrolled usage. |
defaultValue? |
MultiSelectValue |
Default values for uncontrolled usage. | |
disabled? |
boolean |
false |
Whether the entire select is disabled. |
display? |
"chips" | "count" |
'count' |
Display mode for the trigger. 'count': Shows "N selected"; 'chips': Shows visual chips for selected items |
id? |
string |
Optional trigger ID override. Useful for Field integration via htmlFor. | |
labels? |
MultiSelectLabels |
Override internal display and screen reader strings for i18n. All keys are optional — omitted keys use English defaults. | |
max? |
number |
Maximum number of selections allowed. When reached, unselected options become disabled. | |
maxChips? |
number |
3 |
Maximum number of chips to show before "+N more" badge. Only applies when display='chips'. |
onMaxReached? |
() => void |
Callback when max selections is reached. | |
onOpenChange? |
(open: boolean) => void |
Callback when open state changes. | |
onValueChange? |
(value: MultiSelectValue) => void |
Callback when selection changes. | |
open? |
boolean |
Controlled open state. | |
placeholder? |
string |
'' |
Placeholder text when no values are selected. |
size? |
"lg" | "md" | "sm" |
'md' |
Control size. |
value? |
MultiSelectValue |
Controlled selected values. |
MultiSelect.Trigger
Also exported as MultiSelectTrigger.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild? |
boolean |
false |
When true, merges props onto the child element instead of wrapping. Child must be a single React element that accepts ref and event handlers. |
children? |
React.ReactNode |
||
className? |
string |
Additional CSS class names. |
MultiSelect.Content
Also exported as MultiSelectContent.
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
||
className? |
string |
Additional CSS class names. |
MultiSelect.Option
Also exported as MultiSelectOption.
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
||
value |
OptionValue |
The value for this option. Required and must be unique. Can be string or number. | |
className? |
string |
Additional CSS class names. | |
disabled? |
boolean |
false |
Whether this option is disabled. |
textValue? |
string |
Text value for typeahead and chip labels. Required when children is not a string. If children is a string, defaults to that. |
MultiSelect.Group
Also exported as MultiSelectGroup.
| Prop | Type | Default | Description |
|---|---|---|---|
children? |
ReactNode |
||
className? |
string |
MultiSelect.Label
Also exported as MultiSelectLabel.
| Prop | Type | Default | Description |
|---|---|---|---|
children? |
ReactNode |
||
className? |
string |
Hooks
useMultiSelect
useMultiSelect(): MultiSelectActionsContextValue & MultiSelectStateContextValue
Combined hook for components that need both. Convenience for Trigger/Content that need everything.
Returns:
| Member | Type | Description |
|---|---|---|
disabled |
boolean |
|
placeholder |
string |
|
display |
"chips" | "count" |
|
maxChips |
number |
|
max |
number | undefined |
|
size |
"lg" | "md" | "sm" |
|
labels |
Required<MultiSelectLabels> |
|
triggerId |
string |
|
listboxId |
string |
|
ariaLabel? |
string |
|
ariaLabelledBy? |
string |
|
ariaDescribedBy? |
string |
|
ariaInvalid? |
"false" | "true" | boolean |
|
ariaRequired? |
"false" | "true" | boolean |
|
setOpen |
(open: boolean) => void |
|
toggleOption |
(optionValue: OptionValue, textValue: string) => void |
|
clearAll |
() => void |
|
registerOption |
(option: RegisteredOption) => void |
|
unregisterOption |
(value: OptionValue) => void |
|
refs |
{ reference: React.RefObject<ReferenceType | null>; floating: React.RefObject<HTMLElement | null>; setReference: (node: ReferenceType | null) => void; setFloating: (node: HTMLElement | null) => void; } |
|
listRef |
MutableRefObject<(HTMLElement | null)[]> |
|
getReferenceProps |
(userProps?: React.HTMLProps<Element>) => Record<string, unknown> |
|
getFloatingProps |
(userProps?: React.HTMLProps<HTMLElement>) => Record<string, unknown> |
|
getItemProps |
(userProps?: React.HTMLProps<HTMLElement>) => Record<string, unknown> |
|
open |
boolean |
|
value |
MultiSelectValue |
|
maxReached |
boolean |
|
isSelected |
(optionValue: OptionValue) => boolean |
|
getSelectedOptions |
() => RegisteredOption[] |
|
activeIndex |
number | null |
|
highlightedValue |
OptionValue | null |
|
orderedOptions |
RegisteredOption[] |
|
floatingStyles |
CSSProperties |
|
floatingContext |
FloatingContext |
RegisteredOption (the option of registerOption):
| Member | Type | Description |
|---|---|---|
value |
OptionValue |
|
ref |
RefObject<HTMLElement | null> |
|
disabled |
boolean |
|
textValue |
string |
Examples
-
With Field Error: MultiSelect inside Field with Field.Error —
aria-invalidlands on the trigger, the element wearingtui-input-group, so the shared primitive lights the danger border and the error text is announced.
Other stories: Basic, Chips Display Mode, Chips with Overflow, Default Value, Numeric Values, Max Selections, Disabled, Disabled Options, Groups, With Icons, Controlled, In Modal, Keyboard Navigation, Long List, Form Example, Sizes, Controlled Open, Static (Inspect) (interactive page).
Component API tokens
Read via fallback, never declared. Set one on an ancestor to restyle every instance under it.
--tui-multiselect-min-width--tui-multiselect-trigger-bg--tui-multiselect-trigger-fg--tui-multiselect-trigger-border--tui-multiselect-trigger-border-hover--tui-multiselect-trigger-border-focus--tui-multiselect-trigger-radius--tui-multiselect-content-bg--tui-multiselect-content-fg--tui-multiselect-content-border--tui-multiselect-content-shadow--tui-multiselect-content-radius--tui-multiselect-option-radius--tui-multiselect-option-bg-hover--tui-multiselect-option-bg-selected--tui-multiselect-option-fg-hover--tui-multiselect-option-fg-selected--tui-multiselect-option-bg-selected-hover--tui-multiselect-option-gap--tui-multiselect-chip-bg--tui-multiselect-chip-fg