Multi Select
The MultiSelect component lets users select multiple options from a dropdown list. Selected options appear as removable tags, and users can optionally create new options by typing. It’s suited to scenarios where more than one value is needed — tags, frameworks, labels, and similar.
Variations
Disallow creation
The MultiSelect component accepts an optional disallowCreation prop.
When set to true, it disables the ability to add new items that are not part of the existing options list.
Form integration
The MultiSelect component integrates seamlessly with form elements like labels, captions, and validation messages. A non-empty error sets the theme to danger; a non-empty warning sets it to warning.
With tooltip
tooltipContent is used to display the content of the tooltip. tooltipProps is used to pass additional props to the tooltip.
Orientation
The MultiSelect component accepts an optional orientation prop, which defines the layout direction of the input and its label.
Available options are horizontal and vertical (default).
Installation
import { MultiSelect, MultiSelectOption } from "@harnessio/ui/components";import { useState } from "react";
// Basic usage with controlled componentconst MyComponent = () => { const [selected, setSelected] = useState<MultiSelectOption[]>([]);
return ( <MultiSelect label="Select frameworks" placeholder="Select options..." options={[ { key: "react", id: "react" }, { key: "vue", id: "vue" }, { key: "angular", id: "angular" }, ]} value={selected} onChange={setSelected} /> );};Anatomy
MultiSelect is built on the cmdk command primitive. It renders a labeled input with a dropdown of options; chosen items show as removable tags inside the field.
<MultiSelect label="Frameworks" options={[{ key: "React", id: "react" }]} value={selected} onChange={setSelected}/>- Options — each option is a
MultiSelectOptionwith a requiredidandkey.keyis used as the tag label and the default dropdown label; passlabelto customize the dropdown row (selected tags still usekey). - Controlled vs uncontrolled — pass
value+onChangefor a controlled selection; omitvalueand seed withdefaultValue(default[]) for uncontrolled use. - Creating options — typing a value and pressing Enter creates a new option (comma-separated input creates several).
creationValueModecontrols parsing:keyValue(default) splitskey:valuepairs,literaluses the raw text as-is. SetdisallowCreationto turn this off — the empty dropdown then shows “No results found” instead of the creation hint. - Validation & theme —
theme(default/danger/warning) styles the field. A non-emptyerrorstring forces thedangertheme and shows the error caption; a non-emptywarningforceswarning. Otherwisecaptionshows as helper text. - Search — filtering is client-side by default (matched against
key). ProvidesearchQuery+setSearchQueryto drive search externally (e.g. server-side); the built-in filter is then bypassed. - Layout & label —
label,caption,optional, and tooltip (tooltipContent/tooltipProps) render around the field.orientation(verticaldefault /horizontal) sets whether the label sits above or beside the input. - Other states —
disableddisables the field and hides tag remove icons;isLoadingshows a loading skeleton inside the open dropdown;maxSelectedisn’t supported.
Best practices
Do
- Give every option a stable, unique
id— removal, replacement, and change detection key off it. - Use
disallowCreationwhen the value set is fixed (a known enum) and free-form entries would be invalid. - Prefer
error/warningstrings over settingthemedirectly, so the validation message and the color stay in sync.
Don’t
- Don’t use
MultiSelectfor a single choice — reach for Select instead. - Don’t overload the list with hundreds of ungrouped options without search; wire up
searchQuery/setSearchQueryfor large, server-backed sets.
Content
- Write a
labelthat names what’s being collected (“Frameworks”, “Labels”) and aplaceholderthat hints at the action (“Select frameworks you like…”). - Keep option
keys short and human-readable — they’re what users see as tags.
Accessibility
Keyboard
- Backspace / Delete on an empty input removes the last selected tag.
- Enter creates an option from the typed text (unless
disallowCreationis set). - Escape blurs the input and closes the dropdown.
Labeling
labelis tied to the input viahtmlFor/id, so assistive tech announces the field name and clicking the label focuses the input.placeholderis also applied as the field’saria-label.
Focus & states
- The dropdown opens on input focus and closes on outside click or blur. Selected tags expose a remove control (hidden when
disabled).
Known limitations
- Not a semantic combobox/listbox. The field container uses
role="textbox"rather than the ARIA combobox/listbox pattern — it doesn’t setaria-expanded,aria-activedescendant, or listbox roles. Screen-reader users may not get the full “N of M, expanded” combobox experience.
Properties
MultiSelect
Prop | Required | Default | Type |
|---|---|---|---|
| label | false | string | |
| caption | false | string | |
| options | false | MultiSelectOption[] | |
| value | false | MultiSelectOption[] | |
| defaultValue | false | [] | MultiSelectOption[] |
| onChange | false | (options: MultiSelectOption[]) => void | |
| placeholder | false | string | |
| searchQuery | false | string | null | |
| setSearchQuery | false | (query: string) => void | |
| disallowCreation | false | false | boolean |
| creationValueMode | false | 'keyValue' | 'keyValue' | 'literal' |
| creationLabel | false | 'Press Enter to create' | string |
| isLoading | false | false | boolean |
| disabled | false | boolean | |
| optional | false | false | boolean |
| theme | false | 'default' | 'default' | 'danger' | 'warning' |
| error | false | string | |
| warning | false | string | |
| tooltipContent | false | Exclude<ReactNode, boolean | null | undefined> | |
| tooltipProps | false | IconWithTooltipProps | |
| orientation | false | 'vertical' | 'vertical' | 'horizontal' |
| prefix | false | ReactNode | |
| className | false | string | |
| wrapperClassName | false | string | |
| commandProps | false | ComponentPropsWithoutRef<typeof Command.Root> | |
| inputProps | false | Omit<CommandInputProps, 'value' | 'placeholder' | 'disabled'> |
MultiSelectOption
interface MultiSelectOption { id: string | number; // Required — unique identifier for the option key: string; // Required — tag label and default dropdown label value?: string; // Optional — secondary value for key-value pairs label?: ReactNode; // Optional — custom dropdown content; selected tags still use `key` icon?: IconV2NamesType; // Optional — icon shown in the dropdown item title?: string; // Optional — title attribute for the option disable?: boolean; // Optional — disables the option in the dropdown theme?: TagTheme; // Optional — tag color for the selected item onReset?: () => void; // Optional — called when the option is removed}