Skip to content

MultiSelect

Multi-select dropdown field with checkbox-style options.

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} />
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} />
function EmptyMultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) {
const [value, setValue] = useState<string[]>([]);
return (
<MultiSelect
{...args}
hasClear
onChange={setValue}
startIcon={Columns3}
value={value}
/>
);
}
<EmptyMultiSelectStory {...args} />
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} />
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>
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} />
function EmptyMultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) {
const [value, setValue] = useState<string[]>([]);
return (
<MultiSelect
{...args}
hasClear
onChange={setValue}
startIcon={Columns3}
value={value}
/>
);
}
<EmptyMultiSelectStory {...args} />
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} />
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} />
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} />
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} />
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} />
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} />
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} />
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} />
Choose which columns are visible in the table.
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} />
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} />
function EmptyMultiSelectStory(args: React.ComponentProps<typeof MultiSelect>) {
const [value, setValue] = useState<string[]>([]);
return (
<MultiSelect
{...args}
hasClear
onChange={setValue}
startIcon={Columns3}
value={value}
/>
);
}
<EmptyMultiSelectStory {...args} />
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} />
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} />
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} />

Multi-select dropdown field with checkbox-style options.

When isOptional: false, isRequired: false

PropTypeDefaultDescription
children(option: MultiSelectOptionData) => ReactNode—Custom render function for selectable options.
classNamestring—Additional CSS class names applied to the field root.
data-testidstring—Test ID applied to the combobox button.
descriptionReactNode—Supporting text displayed below the label.
emptyOptionsTextstring'No options'Text shown in the menu when there are no options at all.
emptySearchResultsTextstring'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.
hasClearbooleanfalseWhether to show a clear button when values are selected.
htmlNamestring—HTML name attribute for native form submission. Each selected value is submitted as a separate entry.
hasEntriesOnFocusbooleanfalseWhether focusing the trigger opens the option list. Pointer presses keep their ordinary click-to-toggle behaviour.
hasSearchbooleanfalseWhether to show search input in the dropdown.
indicatorPosition"start" | "end"'start'Which logical edge of each option shows the selection checkbox.
hasSelectAllbooleanfalseWhether to show a select-all option.
isDefaultOpenbooleanfalseWhether the selector starts open.
isDisabledbooleanfalseWhether the selector is disabled.
isReadOnlybooleanfalseWhether the values are displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
isLoadingbooleanfalseWhether the selector is loading.
label*string—Field label.
labelEndReactNode—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.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip content shown next to the label.
maxBadgesnumber3Maximum number of badges before showing an overflow count.
onChange*(value: string[]) => void—Called when selection changes.
options*ReadonlyArray<MultiSelectOption>—Options to display.
placeholderstring'Select...'Placeholder shown when no values are selected.
refRef<HTMLButtonElement>—Ref forwarded to the combobox button.
searchPlaceholderstring'Search...'Search input placeholder.
selectAllLabelstring'Select all'Select-all option label.
size"sm" | "md" | "lg"'md'Select size.
startIconIconComponent—Start icon rendered in the trigger.
statusInputStatus—Validation status displayed below the selector.
styleCSSProperties—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.
isOptionalfalse—
isRequiredfalse—

When isOptional: true, isRequired: false

PropTypeDefaultDescription
children(option: MultiSelectOptionData) => ReactNode—Custom render function for selectable options.
classNamestring—Additional CSS class names applied to the field root.
data-testidstring—Test ID applied to the combobox button.
descriptionReactNode—Supporting text displayed below the label.
emptyOptionsTextstring'No options'Text shown in the menu when there are no options at all.
emptySearchResultsTextstring'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.
hasClearbooleanfalseWhether to show a clear button when values are selected.
htmlNamestring—HTML name attribute for native form submission. Each selected value is submitted as a separate entry.
hasEntriesOnFocusbooleanfalseWhether focusing the trigger opens the option list. Pointer presses keep their ordinary click-to-toggle behaviour.
hasSearchbooleanfalseWhether to show search input in the dropdown.
indicatorPosition"start" | "end"'start'Which logical edge of each option shows the selection checkbox.
hasSelectAllbooleanfalseWhether to show a select-all option.
isDefaultOpenbooleanfalseWhether the selector starts open.
isDisabledbooleanfalseWhether the selector is disabled.
isReadOnlybooleanfalseWhether the values are displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
isLoadingbooleanfalseWhether the selector is loading.
label*string—Field label.
labelEndReactNode—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.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip content shown next to the label.
maxBadgesnumber3Maximum number of badges before showing an overflow count.
onChange*(value: string[]) => void—Called when selection changes.
options*ReadonlyArray<MultiSelectOption>—Options to display.
placeholderstring'Select...'Placeholder shown when no values are selected.
refRef<HTMLButtonElement>—Ref forwarded to the combobox button.
searchPlaceholderstring'Search...'Search input placeholder.
selectAllLabelstring'Select all'Select-all option label.
size"sm" | "md" | "lg"'md'Select size.
startIconIconComponent—Start icon rendered in the trigger.
statusInputStatus—Validation status displayed below the selector.
styleCSSProperties—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—
isRequiredfalse—

When isOptional: false, isRequired: true

PropTypeDefaultDescription
children(option: MultiSelectOptionData) => ReactNode—Custom render function for selectable options.
classNamestring—Additional CSS class names applied to the field root.
data-testidstring—Test ID applied to the combobox button.
descriptionReactNode—Supporting text displayed below the label.
emptyOptionsTextstring'No options'Text shown in the menu when there are no options at all.
emptySearchResultsTextstring'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.
hasClearbooleanfalseWhether to show a clear button when values are selected.
htmlNamestring—HTML name attribute for native form submission. Each selected value is submitted as a separate entry.
hasEntriesOnFocusbooleanfalseWhether focusing the trigger opens the option list. Pointer presses keep their ordinary click-to-toggle behaviour.
hasSearchbooleanfalseWhether to show search input in the dropdown.
indicatorPosition"start" | "end"'start'Which logical edge of each option shows the selection checkbox.
hasSelectAllbooleanfalseWhether to show a select-all option.
isDefaultOpenbooleanfalseWhether the selector starts open.
isDisabledbooleanfalseWhether the selector is disabled.
isReadOnlybooleanfalseWhether the values are displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
isLoadingbooleanfalseWhether the selector is loading.
label*string—Field label.
labelEndReactNode—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.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip content shown next to the label.
maxBadgesnumber3Maximum number of badges before showing an overflow count.
onChange*(value: string[]) => void—Called when selection changes.
options*ReadonlyArray<MultiSelectOption>—Options to display.
placeholderstring'Select...'Placeholder shown when no values are selected.
refRef<HTMLButtonElement>—Ref forwarded to the combobox button.
searchPlaceholderstring'Search...'Search input placeholder.
selectAllLabelstring'Select all'Select-all option label.
size"sm" | "md" | "lg"'md'Select size.
startIconIconComponent—Start icon rendered in the trigger.
statusInputStatus—Validation status displayed below the selector.
styleCSSProperties—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.
isOptionalfalse—
isRequired*true—