formsmulti-select
MultiSelect
Dropdown select component with search, multi-select, and custom item rendering.
Import#
Slot Structure#
Tag container with inline input and a floating listbox with grouped options.
Control#
Listbox#
Examples#
Multiple Select#
Multiple selection with tags and allowClear.
Token Separators#
Create and select tags when a separator is typed.
Create New Tags#
Type a new value and press Enter or click Create in the empty state.
Max Count & Max Tag Count#
Limit selections and visible chips.
Virtual Rendering#
Import useListVirtualizer from moraine/utils to render only visible entries. Pass its virtualRender to MultiSelect and forward scrollToItem to its scrollToIndex method so keyboard highlighting can reveal off-screen options.
Install the adapter’s optional peer dependency before using it:
API Reference#
Attributes#
Slot
root5 attributesSelect root that owns open state, value display, and popup positioning.
Data Attributes
| Data Attribute | Description |
|---|---|
| data-disabled | Present when the component or item is disabled. |
| data-invalid | Present when the field has a validation error. |
| data-required | Present when the field is required. |
| data-size | Stores the resolved size variant. |
| data-variant | Stores the resolved visual variant. |
Props#
| Prop | Type | Default | Description |
|---|---|---|---|
| allowClear | boolean | undefined | false | Show a clear button when a value is selected. |
| allowCreate | boolean | undefined | — | Allow creating new tags on Enter when no match is found. |
| class | ClassValue | — | Class applied to the component root or trigger element. |
| classes | MultiSelectT.Classes | undefined | — | — |
| closeIcon | IconT.Name | — | Icon used when the action button clears the selection.
Tag remove buttons keep using this icon as well. |
| defaultOpen | boolean | undefined | false | Initial open state. |
| defaultSearchValue | string | undefined | — | Default search value. |
| defaultValue | TItem[] | undefined | — | The default value of the input (uncontrolled). |
| disabled | boolean | undefined | false | Whether the input is disabled. |
| emptyRender | ComponentOrElement<MultiSelectT.EmptyRenderProps<TItem>> | undefined | — | Custom renderer for the empty state when current filtered result has no matches. |
| filterOption | boolean | "startsWith" | "endsWith" | "contains" | ((inputValue: string, option: MultiSelectT.Item<TItem>) => boolean) | undefined | true | Filter function or boolean. `false` disables filtering. |
| gutter | number | undefined | 0 | Gap (px) between the control and popup content. |
| id | string | undefined | — | The ID of the input element. |
| itemProps | ((option: MultiSelectT.Item<TItem> & BaseSelectT.OptionRenderState) => ElementProps<HTMLDivElement> | undefined) | undefined | — | Additional attributes for an option row. |
| labelRender | ComponentOrElement<MultiSelectT.LabelRenderProps<TItem>> | undefined | — | Custom renderer for the option label text. |
| leadingIcon | IconT.Name | — | Icon shown before the input/value area. |
| listboxProps | ElementProps<HTMLDivElement> | undefined | — | Additional attributes for the listbox element. |
| loading | boolean | undefined | — | Whether the select is in a loading state. |
| loadingIcon | IconT.Name | icon-loading | Icon shown during loading state. |
| maxCount | number | undefined | — | Maximum number of selected values (multiple/tags). |
| maxTagCount | number | undefined | — | Maximum visible tags before showing +N (visual only). |
| name | string | undefined | — | The name of the input element, used for form submission. |
| onChange | ((value: NoInfer<TItem[]>) => void) | undefined | — | Called when the selection changes. |
| onClear | (() => void) | undefined | — | Called when clear is triggered. |
| onOpenChange | ((open: boolean) => void) | undefined | — | Called whenever the popup open state changes. |
| onScrollBottom | (() => void) | undefined | — | Called when the listbox is scrolled to bottom. Useful for infinite loading scenarios. Make sure to set `overflowPadding` and `scrollBottomThreshold` appropriately to ensure the callback is triggered at the right time. |
| onSearch | ((value: string) => void) | undefined | — | Called when the search input changes. |
| open | boolean | undefined | — | Controlled open state. |
| optionRender | ComponentOrElement<MultiSelectT.OptionRenderProps<TItem>> | undefined | — | Custom renderer for each option in the dropdown. Passes `null` for empty state. |
| options | MultiSelectT.Item<TItem>[] | undefined | — | Available options. |
| overflowPadding | number | undefined | 4 | Padding (px) used when calculating popup overflow and viewport collision. |
| placeholder | string | undefined | — | Placeholder text shown when no value is selected. |
| ref | JSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never | undefined | — | — |
| required | boolean | undefined | false | Whether the input is required. |
| scrollBottomThreshold | number | undefined | 20 | Distance (px) from the bottom at which onScrollBottom fires. |
| scrollToItem | ((item: MultiSelectT.Item<TItem>, entryIndex: number) => void) | undefined | — | Scrolls a highlighted option into view using its flattened entry index. |
| search | boolean | undefined | false | Enable search input. |
| searchMaxLength | number | undefined | — | Maximum search text length applied on final commit. |
| searchValue | string | undefined | — | Controlled search value. |
| size | "xs" | "sm" | "md" | "lg" | "xl" | undefined | — | — |
| style | JSX.CSSProperties | undefined | — | — |
| styles | MultiSelectT.Styles | undefined | — | — |
| tagRender | ComponentOrElement<MultiSelectT.TagRenderProps<TItem>> | undefined | — | Custom renderer for each selected tag (multiple/tags). |
| tagVariant | "default" | "outline" | "solid" | undefined | — | Variant for the selected tags. |
| tokenSeparators | string[] | undefined | — | Characters that split input into tokens and immediately select them. |
| trailingIcon | IconT.Name | icon-chevron-down | Icon used when the action button opens the dropdown. |
| value | TItem[] | undefined | — | The current value of the input (controlled). |
| variant | "none" | "outline" | "ghost" | "subtle" | undefined | — | — |
| virtualRender | Component<MultiSelectT.VirtualRenderProps<TItem>> | undefined | — | Renders flattened group labels and options through a virtualization layer. |
Items#
| Prop | Type | Default | Description |
|---|---|---|---|
| children | Omit<BaseSelectT.Item<MultiSelectT.Value>, "children">[] | undefined | — | One-layer child options for grouped select. |
| description | string | JSX.Element | — | Description shown below the label. |
| disabled | boolean | undefined | — | Whether the option is disabled. |
| icon | IconT.Name | — | Icon shown next to the label. |
| key | string | undefined | — | Text key used for filtering and matching; set this when `label` is not a string. |
| label | string | JSX.Element | — | Label to display for the option, or the option group title. |
| value | MultiSelectT.Value | undefined | — | Value of the option. |