MultiSelect

MultiSelect

A dropdown component that allows users to select multiple options from a list, with support for search, chips, and validation states.


Import

tsx
import { MultiSelect } from "asheeui";

Usage

tsx
"use client";
import { MultiSelect } from "asheeui";
import { useState } from "react";
const options = [
{ label: "React", value: "react" },
{ label: "Vue", value: "vue" },
{ label: "Angular", value: "angular" },
];
export default function Basic() {
const [selected, setSelected] = useState<(string | number)[]>([]);
return (
<div className="w-full max-w-md">
<MultiSelect options={options} value={selected} onChange={setSelected} />
</div>
);
}

Examples

Custom chip display

Selected Items
React

Disable chip display


Inheritance

MultiSelect extends the <Select /> component and inherits all of its props and functionality. This means you can use any Select prop with MultiSelect, including:

  • size, radius, variant, color
  • label, labelAlign, description, message
  • status, required, isLoading, disabled
  • startContent, endContent, className, and all native field attributes
  • options, placeholder, isSearch, searchPlaceholder, belowList
  • menu configuration for dropdown appearance

Reference: For a complete list of inherited props with detailed descriptions, see the Select documentation.

What MultiSelect adds

MultiSelect extends Select with the following additional features:

FeatureDescription
Multi-SelectionAllows selecting multiple options from the list
Chip DisplaySelected options are displayed as removable chips below the trigger
Chip ConfigurationFull control over chip appearance via the chip prop
Toggle SelectionClicking an option toggles its selection state
Selection CountShows the number of selected items in the trigger

What MultiSelect modifies

ModificationDescription
valueArray of selected values instead of a single value
onChangeReceives an array of selected values
placeholderReplaced with InputLabel - shows when no items selected
Selection BehaviorToggle selection instead of replacing selection

What MultiSelect does not inherit

The following Select props are not available on MultiSelect:

PropReason
onValueChangeReplaced with onChange which receives an array
initialValueNot applicable - use value with array
placeholderReplaced with InputLabel

All other Select props are fully supported. See the Select documentation for the complete list.


Props

PropTypeDefaultDescription
optionsSelectMenuOption[][]Array of options to display
value(string | number)[]NoneControlled selected values
onChange(values: (string | number)[]) => voidNoneCallback when selection changes
InputLabelstring"Select Options..."Label displayed when no items selected
isSearchbooleantrueEnable search functionality
searchPlaceholderstring"Search..."Search input placeholder
searchInputNamestring"multiselect-search"Search input name attribute
belowListReactNodeNoneContent to display below the option list
disableChipDisplaybooleanfalseHide the selected chips display area
chipLabelstringNoneLabel above the chips section
chipChipConfigNoneChip configuration overrides (see below)
menuMenuConfigNoneMenu configuration overrides (see below)
containerClassNamestringNoneExtra classes for container

Chip Configuration

The selected chips appearance is controlled through the chip prop:

PropTypeDefaultDescription
chip.variant"solid" | "ghost" | "bordered" | "faded" | "underlined""solid"*Visual style of chips
chip.color"none" | "primary" | "secondary" | "danger" | "warning" | "success""primary"*Color of chips
chip.radius"none" | "xs" | "sm" | "md" | "lg" | "xl" | "full""sm"*Corner rounding of chips
chip.size"sm" | "md" | "lg""sm"*Size of chips

The dropdown menu appearance is controlled through the menu prop, which accepts all MenuConfig options:

PropTypeDefaultDescription
menu.radiusRadius"md"*Corner rounding of the dropdown menu
menu.sizeSize"md"*Size of menu items
menu.itemVariantVariant"ghost"*Visual style of inactive options
menu.itemColorColor"primary"*Color of inactive options
menu.activeItemVariantVariant"faded"*Visual style of selected option
menu.activeItemColorColor"primary"*Color of selected option
menu.lockScrollbooleanfalse*Whether to lock body scroll when open
menu.portalbooleantrue*Whether to render menu in a portal
menu.portalTargetHTMLElement | nullnull*Custom portal target element
menu.classNamestringNoneExtra classes for the dropdown menu

SelectMenuOption

PropTypeDescription
labelstringDisplay text for the option
valuestring | numberValue of the option
disabledbooleanPrevents selection of this option
[key: string]unknownAdditional custom properties

Global Configuration

MultiSelect reads defaults from four places, in this order of precedence:

  1. Instance prop: set directly on <MultiSelect />
  2. Component config: components.multiSelect in your ashee.config
  3. Theme default: defaultVariant / defaultColor / defaultRadius in your ashee.config
  4. Built-in fallback: component's internal default values

Component config

ts
// ashee.config.ts
import type { ExternalConfig } from "asheeui";
export const config: ExternalConfig = {
components: {
multiSelect: {
size: "md",
radius: "md",
variant: "bordered",
color: "primary",
labelAlign: "left",
menu: {
itemVariant: "ghost",
itemColor: "primary",
activeItemVariant: "faded",
activeItemColor: "primary",
radius: "md",
size: "md",
portal: true,
lockScroll: false,
},
chip: {
variant: "solid",
color: "primary",
radius: "sm",
size: "sm",
},
},
},
};

Built-in fallbacks

ts
{
size: "md",
radius: "md",
variant: "bordered",
color: "primary",
status: "default",
labelAlign: "left",
menu: {
itemVariant: "ghost",
itemColor: "primary",
activeItemVariant: "faded",
activeItemColor: "primary",
radius: "md",
size: "md",
portal: true,
lockScroll: false,
},
chip: {
variant: "solid",
color: "primary",
radius: "sm",
size: "sm",
},
}

Accessibility

  • Renders with proper ARIA roles (combobox, listbox)
  • Uses aria-expanded and aria-haspopup for state indication
  • Uses aria-invalid for error states
  • Chips include accessible remove buttons with aria-label
  • Supports keyboard navigation and selection
  • disabled items are not focusable or selectable
  • Implements focus-visible indicators

Portal Behavior

The MultiSelect dropdown menu is rendered in a React portal by default. This means the menu is attached to document.body rather than staying in the component's DOM hierarchy.

Why use a portal?

  • Escapes CSS containment: The menu appears above other content even when inside containers with overflow: hidden or contain: layout
  • Avoids stacking context issues: The menu maintains proper z-index regardless of parent stacking contexts
  • Works with any parent: The menu functions correctly regardless of where the MultiSelect is placed in the component tree
  • Prevents clipping: The menu is never clipped by parent containers

When to disable the portal

You may want to disable the portal (by setting menu.portal={false}) when:

  • You need the menu to stay within a specific container for testing purposes
  • You are rendering inside a shadow DOM or iframe where document.body is not appropriate
  • You have specific layout requirements that depend on the menu remaining in the DOM hierarchy

You can also provide a custom menu.portalTarget to render the menu into a specific container instead of document.body.


Notes

  • Controlled Usage: Use value/onChange for controlled behavior.
  • Chip Display: Selected options are displayed as removable chips below the trigger button.
  • Search: The search input filters options in real-time. Disable with isSearch={false}.
  • Custom Value Handling: The component accepts both controlled (via value/onChange) and custom chip handlers (chipOptions, handleRemoveChip, handleAddChip).
  • Below List: The belowList prop is useful for adding "Add new" buttons or additional controls.
  • Portal: The dropdown menu is portaled to document.body by default. This can be disabled via the menu.portal prop or component config.
  • Inheritance: MultiSelect inherits all Select props but modifies the selection behavior for multiple values.
Previous

← Modal

Next

PasswordInput →