Skip to content

API

Prop Type Default Description
options (T | MultiSelectGroup<T>)[] [] Options, groups and submenus. T is MultiSelectOption, or any shape with getLabel and getValue, whose disabled, style, classes and type keep their meaning.
defaultValue string[] Initial selection, as option values. Resolves once options load; later changes are ignored (use value, or a new key to reset). Values not in options are left out, or listed under Custom Values with customValue.
value string[] Controlled selection, as option values. Update it from onChange.
onChange (values, options) => void Called after every change with the selected values (string[]) and their option objects (T[], so MultiSelectOption[] by default). A created value’s option is { label: value, value }.
getLabel (option) => ReactNode opt => opt.label What an option shows in the menu, chips and single value. Search and the tooltip use it when it returns text, else the option’s label. See custom labels.
getValue (option) => string opt => opt.value An option’s value, as used by value, defaultValue and onChange. With getLabel, options can have any shape.
filterOption (option, search) => boolean label text or value contains it Whether an option matches the search text (trimmed, commas removed). Override for fuzzy or multi-field search: (user, s) => user.email.includes(s). Not called for created values.
singleSelect boolean false Allow one value only.
customValue boolean false Create options by typing and pressing Enter or comma.
createLabel (text) => string text => `Create "${text}"` With customValue, the label of the option that creates the typed text.
customValuesTitle ReactNode 'Custom Values' Title of the group listing created values.
limit number | null null Maximum number of values in multi select. The wrapper gets msl-limit-reached once it is hit, and unselected options aria-disabled and msl-option-disable.
closeOnSelect boolean unset Close the menu after a pick. Unset: single select closes, multi select stays open.
placeholder string 'Select...' Placeholder text.
disableChip boolean false Show one label, or a count, instead of chips.
chipAlternateText ReactNode 'Item Selected' Text after the count with disableChip: “3 Item Selected”.
customChip (props) => ReactNode msl-chip markup Renders each chip. props is MultiSelectChipProps: label (what getLabel returns), value, option, disabled, classNames (the select’s, {} when unset) and onDelete(). A render function, so no hooks in it: for state, render <MyChip key={p.value} {...p} />. Chips are keyed by position, so that key resets a chip’s state when its value moves, instead of handing it to the next value. onDelete does nothing while disabled. Clicks and keys inside it don’t open the menu. When a keyboard delete removes the focused element, focus moves to the search field, or to the select when there is none.
disableSearch boolean false Hide the search input.
searchInMenu boolean false Put the search field at the top of the open menu, not after the values: the closed control shows only the values, or placeholder. Single select and disableChip can then search with a value selected.
searchPlaceholder string 'Search...' The search field’s placeholder with searchInMenu.
deleteOnBackspace boolean true Backspace in the empty search field removes the last chip. false: only the chip delete buttons remove chips.
renderList (list) => ReactNode Renders the open list’s rows yourself, through the virtual list library of your choice for thousands of options: (list) => <VirtualList {...list} />. list is MultiSelectListProps: count, row(index) (only the rows you render get built) and activeIndex (the keyboard highlight’s row, -1 for none: scroll it into view). A render function, so no hooks in it. Rows are options, group titles and empty groups’ labels, groups flattened; a row in a group comes in a msl-grp box per group around it. An open submenu’s rows go through it too, as a list of their own in its popover. The listbox and submenus then have data-custom, flex columns in the stylesheet, so a scroller in them shrinks to the menu’s height. Not called while emptyDataLabel shows.
clearLabel string 'Clear' The clear button’s aria-label.
clearable boolean true Show the clear button.
downArrow boolean true Show the down arrow.
downArrowIcon ReactNode SVG Custom down arrow.
closeIcon ReactNode SVG Custom clear icon.
groupIcon ReactNode chevron SVG Icon after a collapsible group’s title, in .msl-expand-icn (classNames.groupIcon). Draw it pointing to the inline end: the stylesheet turns it down while the group is open, and to the left right to left. See Expand icons.
submenuIcon ReactNode chevron SVG Icon after a submenu’s title, in .msl-expand-icn (classNames.submenuIcon), pointing to the inline end.
renderGroupTitle (props) => ReactNode Renders the title row of a group or a submenu, in place of its title and icon. It gets { group, title, total, selected, open, icon } (MultiSelectGroupTitleProps): total is the options in it, nested groups’ and submenus’ too, disabled ones included, selected how many of those are picked, open whether a collapsible group shows its childs or a submenu is open (true for a plain group), and icon the .msl-expand-icn span, to place or leave out (null for a plain group). A render function, not a component: no hooks in it. See Group titles.
disabled boolean false Disable the dropdown.
emptyDataLabel ReactNode 'No Data Found' Shown when there are no options or no search results.
name string '' Name of the hidden input, for forms. Its value is the comma joined selection.
classNames MultiSelectClassNames Classes for each part, e.g. Tailwind utilities, added after its msl-* class. Keys: control, values, input, search, singleValue, chip, chipDelete, actions, clearButton, arrowButton, menu, group, groupTitle, groupIcon, groupBody, option, submenuIcon, submenu, empty. See Tailwind.
className string Extra class on the wrapper.
style CSSProperties {} Inline style on the wrapper. While the menu is open, it gets the control’s height as min-height.
attr HTMLAttributes Props for the wrapper div, such as id or data-*. Its aria-* also go to whichever element is the combobox: aria-label / aria-labelledby name the dropdown for screen readers, aria-describedby / aria-invalid carry an error.
Prop Called
onChange after any change, with the new values and their options
onMenuOpen when the menu opens
onMenuClose when the menu closes (outside click, Escape, Tab, a pick, scrolled out of view)
onClear after the clear button empties the value
interface MultiSelectOption {
label: string // searched, and the default display
value: string
disabled?: boolean
style?: React.CSSProperties
classes?: string // extra class names
[key: string]: any // your own fields, returned in onChange's options
}
// with getLabel and getValue, options can be any shape T, inferred from `options`
interface MultiSelectGroup<T = MultiSelectOption> {
type: 'group' | 'submenu' // a submenu's title opens childs in a popover beside it
title: ReactNode
childs: (T | MultiSelectGroup<T>)[]
emptyDataLabel?: ReactNode // shown when childs is empty
collapsible?: boolean // 'group' only: the title shows and hides childs
collapsed?: boolean // with collapsible, childs start hidden
}
// what renderList gets
interface MultiSelectListProps {
count: number // rows of the menu or an open submenu: options, titles and empty groups' labels, groups flattened
row: (index: number) => ReactNode // renders one row; nothing past the end
activeIndex: number // the keyboard highlight's row, -1 for none
}
// what renderGroupTitle gets. C: customValue is on. To annotate it without customValue, pass false,
// MultiSelectGroupTitleProps<Person, false>, or group's options are typed Person | { label, value }
interface MultiSelectGroupTitleProps<
T = MultiSelectOption,
C extends boolean = boolean
> {
group: MultiSelectGroup<T | { label: string; value: string }> // as in options; { label, value } for customValue's group of created values, unless C is false
title: ReactNode // the group's title
total: number // options in it, nested groups' and submenus' too, disabled ones included
selected: number // how many of those are picked
open: boolean // a collapsible group shows its childs, a submenu is open; true for a plain group
icon: ReactNode // the .msl-expand-icn span, to place or leave out; null for a plain group
}