EXEPERTAI LAB

Research alpha

Time Machine
EXEPERTAI LAB
GALLERY / COLLECTION
← Browse Astryx gallery
Selector·component·@astryxdesign/core

Multi Selector

A checkbox dropdown for selecting multiple values from a list. Selected items can display as a count, labels, or badges. Use it for filtering or when presenting a finite set of options where multiple choices are needed.

Open in Playground @astryxdesign/core/MultiSelector

Showcases and examples

7 documented examples

Multi Selector

MultiSelector API entry

Preview loads on approachPreview loads on approach
Exact source · multi-selector-showcase
// Copyright (c) Meta Platforms, Inc. and affiliates.

'use client';

import {useState} from 'react';
import {MultiSelector} from '@astryxdesign/core/MultiSelector';

export default function MultiSelectorShowcase() {
  const [value, setValue] = useState<string[]>([]);
  return (
    <div style={{width: 300}}>
      <MultiSelector
        label="Columns"
        options={['Name', 'Email', 'Role', 'Status', 'Created']}
        value={value}
        onChange={setValue}
        placeholder="Select columns..."
      />
    </div>
  );
}

Usage

A checkbox dropdown for selecting multiple values from a list. Selected items can display as a count, labels, or badges. Use it for filtering or when presenting a finite set of options where multiple choices are needed.

  • Use for a moderate, finite set of options where multiple choices are needed.
  • Enable search filtering when the list exceeds ~15 options.
  • Use renderOption for custom option rows; the checkbox affordance remains owned by MultiSelector.
  • Enable select-all when most users will want all or nearly all options selected.
  • Use inside InputGroup only when the control needs a short prefix or suffix addon as part of one decorated input surface; prefer count or labels trigger display so the group stays single-line.
  • Use variant="ghost" when a multi-selector sits in a toolbar with ghost buttons. If validation status is needed there, prefer statusVariant="tooltip" so the toolbar height stays compact.
  • Use presentation="adaptive" when the multi-selector should become a bottom sheet on compact touch screens.
  • Use for single-value selection; use Selector instead.
  • Show more than ~20 options without enabling search.
  • Wrap a disabled MultiSelector in Tooltip to explain why it is disabled; disabled triggers swallow the hover events the wrapper needs. Use the disabledMessage prop instead.

Typed props

PropType and behavior
labelstring · required
Label text for accessibility.
optionsMultiSelectorOptionType[] · required
Array of items: strings, objects with value/label/icon/disabled, dividers, or sections.
valuestring[] · required
Currently selected values.
onChange(value: string[]) => void · required
Callback fired when the selection changes.
changeAction(value: string[]) => void | Promise<void>
Async action on change. Fires after onChange.
placeholderstring · default 'Select...'
Placeholder text shown when no value is selected.
size'sm' | 'md' | 'lg' · default 'md'
Size variant for the selector.
variant'input' | 'ghost' · default 'input'
Visual trigger style. input is the bordered input treatment for forms; ghost is borderless and matches ghost buttons for toolbar usage.
triggerDisplay'count' | 'labels' | 'badges' · default 'count'
How to display selected items in the trigger.
formatValue(items: {value: string; label: string}[]) => string
Formats the trigger text when triggerDisplay="count" or "labels". Receives the selected items (value plus resolved label); the count is items.length. Not used by triggerDisplay="badges".
maxBadgesnumber · default 3
Maximum badges to show before "+N". Only for triggerDisplay="badges".
hasSelectAllboolean
Whether to show a select-all checkbox.
selectAllLabelstring · default 'Select all'
Label for the select-all checkbox.
hasSearchboolean
Whether to show a search input for filtering options. As the user types, the match count (or "No results found") is announced to screen readers via a polite live region. The search field has built-in affordances: a leading magnifier icon and, once a query is typed, a trailing clear (✕) button that resets the query and returns focus to the input.
searchPlaceholderstring · default 'Search...'
Placeholder text for the search input.
emptyTextReactNode · default 'No options'
Content shown in the dropdown panel when there are no options to show, and announced in a polite live region when the panel opens (a string override is announced verbatim; a richer node falls back to the default text). Not shown while isLoading.
emptySearchTextReactNode · default 'No results found'
Content shown in the dropdown panel when a search query matches no options, and announced in a polite live region at the same time (a string override is announced verbatim; a richer node falls back to the default text).
isDisabledboolean
Disables the selector.
isReadOnlyboolean · default false
Makes the selector read-only: the selected values stay visible, focusable, and included in form submission, and retain their combobox identity with aria-readonly. The selection surface, clear action, and disclosure indicator are removed. Unlike isDisabled, the control is not dimmed. isDisabled takes precedence when both are set.
htmlNamestring
The HTML name attribute for form submissions. Renders one hidden input per selected value, like a native multi-select.
disabledMessagestring
Explains why the selector is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the trigger focusable via aria-disabled (activation stays blocked). Use this instead of wrapping a disabled MultiSelector in Tooltip. Disabled controls swallow the hover events an external Tooltip needs.
isLabelHiddenboolean
Visually hides the label while keeping it accessible.
descriptionstring
Helper text displayed below the label.
isOptionalboolean
Marks the field as optional.
isRequiredboolean
Marks the field as required.
isLoadingboolean
Shows a loading spinner in the trigger.
status{type: 'error' | 'warning' | 'success', message?: string}
Validation status with an optional message.
statusVariant'attached' | 'detached' | 'tooltip' · default 'attached' for input selectors; 'detached' for ghost selectors
How the status message is placed relative to the input. attached overlaps directly below the bordered input and is only valid for the input variant; ghost selectors detach attached status messages by default. Use tooltip for compact toolbar controls.
renderOption(option: MultiSelectorOptionData) => ReactNode
Custom render function for each selectable option in the dropdown. Not called for dividers, sections, or the select-all row.
indicatorPosition'start' | 'end' · default 'start'
Which edge of the option row carries the checkbox. end pushes it to the far edge of the row, including on the select-all row.
presentation'popover' | 'bottom-sheet' | 'adaptive' · default 'popover'
How the option list is presented. adaptive uses a bottom sheet on compact touch screens and an anchored popover otherwise.
widthSizeValue
Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.
startIconIconType | ReactNode
Icon displayed at the start of the selector trigger.
hasClearboolean · default false
Shows a clear button when values are selected.
isDefaultOpenboolean · default false
Whether the dropdown starts open on mount.
xstyleStyleXStyles
StyleX styles for layout customization. Must be a stylex.create() value.

Anatomy

Field · optional

Standalone Field shell that provides the label and optional supporting content; omitted inside InputGroup.

Trigger · required

Painted control that displays the current selection or placeholder and opens the selection surface when editable.

Icon-rendered start icon · optional

Optional leading semantic icon or icon component rendered through Icon.

Caller-rendered start content · optional

Optional arbitrary React content rendered directly at the start of the trigger.

Trigger clear button · optional

Shared clear action that removes every selected value when hasClear is enabled.

Status icon · optional

Status glyph shown in place of the disclosure indicator for attached or tooltip status.

Indicator icon · optional

Trailing chevron shown when status presentation does not replace it; reflects collapsed or expanded state.

Search row · optional

Panel header with a borderless search input and optional clear action.

Search icon · optional

Leading magnifier rendered through Icon inside the search row.

Search clear button · optional

Shared clear action shown in the search row while a query is present.

Option row · optional

Selectable row for an option or the optional select-all choice.

Option checkbox indicator · optional

CheckboxInput indicator that presents each row’s selected, unselected, or indeterminate state.

Option divider · optional

Divider supplied in the public options data to separate adjacent option groups.

Section heading · optional

Visible heading for a labeled group of option rows.

Empty state · optional

Message shown when the shared panel content has no options or no search matches.

Pointer popup · optional

Anchored painted surface that hosts the shared panel content for popover presentation.

Touch sheet heading · optional

Heading above the shared panel content in bottom-sheet presentation.

Touch sheet · optional

BottomSheet surface that hosts the same panel content for bottom-sheet presentation.

Theming

Targets

astryx-multi-selector

Visual props: variant, size, status

States: disabled, readonly

astryx-multi-selector-clear-icon

Deprecated; use input-clear-icon.

astryx-multi-selector-empty-state
astryx-multi-selector-search
astryx-multi-selector-section-heading
astryx-multi-selector-indicator-icon

States: state

astryx-multi-selector-option

Visual props: size

States: select-all, selected, disabled

astryx-multi-selector-popup