MultiSelect
Multi-select dropdown field with checkbox-style options.
Examples
Section titled “Examples”Default
Section titled “Default”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Searchable
Section titled “Searchable”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Entries On Focus
Section titled “Entries On Focus”function EmptyMultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>([]); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<EmptyMultiSelectStory {...args} />Trigger Variants
Section titled “Trigger Variants”function TriggerVariantsStory(args: React.ComponentProps<typeof MultiSelect>) { const [ghostValue, setGhostValue] = useState<string[]>(['name', 'email']); const [buttonValue, setButtonValue] = useState<string[]>(['role']);
return ( <VStack gap={2} width="full"> <Toolbar dividers={['bottom']} label="Ghost trigger example" size="sm" startContent={ <> <Button icon={Filter} label="Filter" variant="ghost" /> <MultiSelect {...args} isLabelHidden label="Column filter" onChange={setGhostValue} value={ghostValue} variant="ghost" /> </> } /> <Toolbar dividers={['bottom']} label="Button trigger example" size="sm" startContent={ <> <Button icon={Filter} label="Filter" variant="secondary" /> <MultiSelect {...args} isLabelHidden label="Column filter" onChange={setButtonValue} value={buttonValue} variant="button" /> </> } /> </VStack> );}
<TriggerVariantsStory {...args} />Indicator Position
Section titled “Indicator Position”Clear Checkbox at start (default)
Clear Checkbox at end
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<VStack gap={4}> <MultiSelectStory {...args} hasSelectAll label="Checkbox at start (default)" /> <MultiSelectStory {...args} hasSelectAll indicatorPosition="end" label="Checkbox at end" /></VStack>Empty Search Results
Section titled “Empty Search Results”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />No Options
Section titled “No Options”function EmptyMultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>([]); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<EmptyMultiSelectStory {...args} />Badges
Section titled “Badges”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Labels
Section titled “Labels”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Disabled Options
Section titled “Disabled Options”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Sections And Dividers
Section titled “Sections And Dividers”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Disabled
Section titled “Disabled”function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Loading
Section titled “Loading”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Validation Status
Section titled “Validation Status”Clear Columns
Choose at least one column.
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Custom Options
Section titled “Custom Options”function CustomOptionsStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'role']); return ( <MultiSelect {...args} hasSearch onChange={setValue} value={value}> {option => ( <SelectOption description={`${option.value}.field`} icon={User} label={option.label ?? option.value} /> )} </MultiSelect> );}
<CustomOptionsStory {...args} />With Description And Tooltip
Section titled “With Description And Tooltip”Choose which columns are visible in the table.
Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Badge Overflow
Section titled “Badge Overflow”Clear Columns
function OverflowBadgesStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>([ 'name', 'email', 'role', 'status', ]); return ( <MultiSelect {...args} hasClear maxBadges={2} onChange={setValue} startIcon={Columns3} triggerDisplay="badges" value={value} /> );}
<OverflowBadgesStory {...args} />Select All
Section titled “Select All”function EmptyMultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>([]); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<EmptyMultiSelectStory {...args} />Read Only
Section titled “Read Only”function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Custom Count
Section titled “Custom Count”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />Custom Label Summary
Section titled “Custom Label Summary”Clear Columns
function MultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) { const [value, setValue] = useState<string[]>(['name', 'email']); return ( <MultiSelect {...args} hasClear onChange={setValue} startIcon={Columns3} value={value} /> );}
<MultiSelectStory {...args} />MultiSelect
Section titled “MultiSelect”Multi-select dropdown field with checkbox-style options.
When isOptional: false, isRequired: false
| Prop | Type | Default | Description |
|---|---|---|---|
children | (option: MultiSelectOptionData) => ReactNode | — | Custom render function for selectable options. |
className | string | — | Additional CSS class names applied to the field root. |
data-testid | string | — | Test ID applied to the combobox button. |
description | ReactNode | — | Supporting text displayed below the label. |
emptyOptionsText | string | 'No options' | Text shown in the menu when there are no options at all. |
emptySearchResultsText | string | 'No results found' | Text shown in the menu when the search query matches no options. |
formatSelectedValue | (items: ReadonlyArray<MultiSelectOptionData>) => string | — | Formats selected items in count and labels trigger modes. Items follow option order, with missing labels defaulting to their values; selected values absent from options are excluded. Not called for badges or when no items match the selection (the placeholder is shown instead). The field label remains the trigger's accessible name. |
hasClear | boolean | false | Whether to show a clear button when values are selected. |
htmlName | string | — | HTML name attribute for native form submission. Each selected value is submitted as a separate entry. |
hasEntriesOnFocus | boolean | false | Whether focusing the trigger opens the option list. Pointer presses keep their ordinary click-to-toggle behaviour. |
hasSearch | boolean | false | Whether to show search input in the dropdown. |
indicatorPosition | "start" | "end" | 'start' | Which logical edge of each option shows the selection checkbox. |
hasSelectAll | boolean | false | Whether to show a select-all option. |
isDefaultOpen | boolean | false | Whether the selector starts open. |
isDisabled | boolean | false | Whether the selector is disabled. |
isReadOnly | boolean | false | Whether the values are displayed without allowing focus or interaction. |
isLabelHidden | boolean | false | Whether to visually hide the label. |
isLoading | boolean | false | Whether the selector is loading. |
label* | string | — | Field label. |
labelEnd | ReactNode | — | Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true. |
labelIcon | IconComponent | — | Icon shown before the label. |
labelTooltip | ReactNode | — | Tooltip content shown next to the label. |
maxBadges | number | 3 | Maximum number of badges before showing an overflow count. |
onChange* | (value: string[]) => void | — | Called when selection changes. |
options* | ReadonlyArray<MultiSelectOption> | — | Options to display. |
placeholder | string | 'Select...' | Placeholder shown when no values are selected. |
ref | Ref<HTMLButtonElement> | — | Ref forwarded to the combobox button. |
searchPlaceholder | string | 'Search...' | Search input placeholder. |
selectAllLabel | string | 'Select all' | Select-all option label. |
size | "sm" | "md" | "lg" | 'md' | Select size. |
startIcon | IconComponent | — | Start icon rendered in the trigger. |
status | InputStatus | — | Validation status displayed below the selector. |
style | CSSProperties | — | Inline styles applied to the field root. |
triggerDisplay | "count" | "labels" | "badges" | 'count' | How selected items are summarized in the trigger. |
value* | string[] | — | Selected option values. |
variant | "button" | "ghost" | "outline" | 'outline' | Visual style of the selector trigger. |
isOptional | false | — | |
isRequired | false | — |
When isOptional: true, isRequired: false
| Prop | Type | Default | Description |
|---|---|---|---|
children | (option: MultiSelectOptionData) => ReactNode | — | Custom render function for selectable options. |
className | string | — | Additional CSS class names applied to the field root. |
data-testid | string | — | Test ID applied to the combobox button. |
description | ReactNode | — | Supporting text displayed below the label. |
emptyOptionsText | string | 'No options' | Text shown in the menu when there are no options at all. |
emptySearchResultsText | string | 'No results found' | Text shown in the menu when the search query matches no options. |
formatSelectedValue | (items: ReadonlyArray<MultiSelectOptionData>) => string | — | Formats selected items in count and labels trigger modes. Items follow option order, with missing labels defaulting to their values; selected values absent from options are excluded. Not called for badges or when no items match the selection (the placeholder is shown instead). The field label remains the trigger's accessible name. |
hasClear | boolean | false | Whether to show a clear button when values are selected. |
htmlName | string | — | HTML name attribute for native form submission. Each selected value is submitted as a separate entry. |
hasEntriesOnFocus | boolean | false | Whether focusing the trigger opens the option list. Pointer presses keep their ordinary click-to-toggle behaviour. |
hasSearch | boolean | false | Whether to show search input in the dropdown. |
indicatorPosition | "start" | "end" | 'start' | Which logical edge of each option shows the selection checkbox. |
hasSelectAll | boolean | false | Whether to show a select-all option. |
isDefaultOpen | boolean | false | Whether the selector starts open. |
isDisabled | boolean | false | Whether the selector is disabled. |
isReadOnly | boolean | false | Whether the values are displayed without allowing focus or interaction. |
isLabelHidden | boolean | false | Whether to visually hide the label. |
isLoading | boolean | false | Whether the selector is loading. |
label* | string | — | Field label. |
labelEnd | ReactNode | — | Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true. |
labelIcon | IconComponent | — | Icon shown before the label. |
labelTooltip | ReactNode | — | Tooltip content shown next to the label. |
maxBadges | number | 3 | Maximum number of badges before showing an overflow count. |
onChange* | (value: string[]) => void | — | Called when selection changes. |
options* | ReadonlyArray<MultiSelectOption> | — | Options to display. |
placeholder | string | 'Select...' | Placeholder shown when no values are selected. |
ref | Ref<HTMLButtonElement> | — | Ref forwarded to the combobox button. |
searchPlaceholder | string | 'Search...' | Search input placeholder. |
selectAllLabel | string | 'Select all' | Select-all option label. |
size | "sm" | "md" | "lg" | 'md' | Select size. |
startIcon | IconComponent | — | Start icon rendered in the trigger. |
status | InputStatus | — | Validation status displayed below the selector. |
style | CSSProperties | — | Inline styles applied to the field root. |
triggerDisplay | "count" | "labels" | "badges" | 'count' | How selected items are summarized in the trigger. |
value* | string[] | — | Selected option values. |
variant | "button" | "ghost" | "outline" | 'outline' | Visual style of the selector trigger. |
isOptional* | true | — | |
isRequired | false | — |
When isOptional: false, isRequired: true
| Prop | Type | Default | Description |
|---|---|---|---|
children | (option: MultiSelectOptionData) => ReactNode | — | Custom render function for selectable options. |
className | string | — | Additional CSS class names applied to the field root. |
data-testid | string | — | Test ID applied to the combobox button. |
description | ReactNode | — | Supporting text displayed below the label. |
emptyOptionsText | string | 'No options' | Text shown in the menu when there are no options at all. |
emptySearchResultsText | string | 'No results found' | Text shown in the menu when the search query matches no options. |
formatSelectedValue | (items: ReadonlyArray<MultiSelectOptionData>) => string | — | Formats selected items in count and labels trigger modes. Items follow option order, with missing labels defaulting to their values; selected values absent from options are excluded. Not called for badges or when no items match the selection (the placeholder is shown instead). The field label remains the trigger's accessible name. |
hasClear | boolean | false | Whether to show a clear button when values are selected. |
htmlName | string | — | HTML name attribute for native form submission. Each selected value is submitted as a separate entry. |
hasEntriesOnFocus | boolean | false | Whether focusing the trigger opens the option list. Pointer presses keep their ordinary click-to-toggle behaviour. |
hasSearch | boolean | false | Whether to show search input in the dropdown. |
indicatorPosition | "start" | "end" | 'start' | Which logical edge of each option shows the selection checkbox. |
hasSelectAll | boolean | false | Whether to show a select-all option. |
isDefaultOpen | boolean | false | Whether the selector starts open. |
isDisabled | boolean | false | Whether the selector is disabled. |
isReadOnly | boolean | false | Whether the values are displayed without allowing focus or interaction. |
isLabelHidden | boolean | false | Whether to visually hide the label. |
isLoading | boolean | false | Whether the selector is loading. |
label* | string | — | Field label. |
labelEnd | ReactNode | — | Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true. |
labelIcon | IconComponent | — | Icon shown before the label. |
labelTooltip | ReactNode | — | Tooltip content shown next to the label. |
maxBadges | number | 3 | Maximum number of badges before showing an overflow count. |
onChange* | (value: string[]) => void | — | Called when selection changes. |
options* | ReadonlyArray<MultiSelectOption> | — | Options to display. |
placeholder | string | 'Select...' | Placeholder shown when no values are selected. |
ref | Ref<HTMLButtonElement> | — | Ref forwarded to the combobox button. |
searchPlaceholder | string | 'Search...' | Search input placeholder. |
selectAllLabel | string | 'Select all' | Select-all option label. |
size | "sm" | "md" | "lg" | 'md' | Select size. |
startIcon | IconComponent | — | Start icon rendered in the trigger. |
status | InputStatus | — | Validation status displayed below the selector. |
style | CSSProperties | — | Inline styles applied to the field root. |
triggerDisplay | "count" | "labels" | "badges" | 'count' | How selected items are summarized in the trigger. |
value* | string[] | — | Selected option values. |
variant | "button" | "ghost" | "outline" | 'outline' | Visual style of the selector trigger. |
isOptional | false | — | |
isRequired* | true | — |