EXEPERTAI LAB

Research alpha

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

Selector

Dropdown selector for choosing from a list of options.

Open in Playground @astryxdesign/core/Selector

Showcases and examples

7 documented examples

Selector

Selector API entry

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

'use client';

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

export default function SelectorShowcase() {
  const [value, setValue] = useState<string | undefined>();
  return (
    <Selector
      style={{width: 300}}
      label="Fruit"
      options={['Apple', 'Banana', 'Orange', 'Mango', 'Pineapple']}
      placeholder="Select a fruit..."
      value={value}
      onChange={setValue}
    />
  );
}

Usage

A dropdown selector for choosing a single value from a list of options. Supports labels, validation, descriptions, and required/optional states. Use it in forms and settings when presenting a moderate number of options. Keyboard typeahead matches a native select: typing on the focused closed trigger selects the matching option directly, repeated presses cycle through options sharing a first letter, and spaces count as match characters ("new y" reaches "New York"). With the menu open, typing moves the highlight and Enter commits. With hasSearch, typing on the closed trigger opens the popup and seeds the search input.

  • Provide a visible label so users understand what they are selecting.
  • Use sections and dividers to organize options when the list exceeds ~8 items.
  • Use renderOption for custom option rows. Do not pass SelectorOption directly as JSX children.
  • Set a meaningful placeholder that hints at the expected selection (e.g. "Choose a country" not "Select...").
  • Use inside InputGroup only when the selector needs a short prefix or suffix addon as part of one decorated input surface.
  • Use variant="ghost" when a 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 selector should become a bottom sheet on compact touch screens.
  • Use for action menus; use Dropdown Menu for triggering commands or navigation.
  • Use when there are only two options; use a SegmentedControl or radio buttons instead.
  • Use Selector for navigation; links should be links, not dropdown options.
  • Use for yes/no or on/off choices; use Switch or CheckboxInput instead.
  • Put more than ~20 options without sections; consider Typeahead for large lists.
  • Wrap a disabled Selector 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.
optionsSelectorOption[] · required
Array of items: strings, objects with value/label/description/icon/disabled, dividers ({type: "divider"}), or sections ({type: "section", title, options}).
valuestring
Currently selected value.
onChange(value: string) => void
Callback fired when the selection changes.
hasClearboolean · default false
Shows a clear (×) button when a value is selected. When true, onChange also accepts null to signal the user cleared the selection.
hasSearchboolean · default false
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).
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.
isDisabledboolean · default false
Disables the selector.
isReadOnlyboolean · default false
Makes the selector read-only: the selected value stays visible, focusable, and included in form submission, and retains its 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 a hidden input carrying the selected value, like a native 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 Selector in Tooltip. Disabled controls swallow the hover events an external Tooltip needs.
isLabelHiddenboolean · default false
Visually hides the label while keeping it accessible.
descriptionstring
Helper text displayed below the label.
isOptionalboolean · default false
Marks the field as optional.
isRequiredboolean · default false
Marks the field as required.
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: SelectorOptionData) => ReactNode
Custom render function for each selectable option in the dropdown. Use this instead of JSX children; dividers and sections are rendered by the selector.
renderValue(option: SelectorOptionData) => ReactNode
Custom render function for the selected option inside the closed trigger. The trigger is sized by padding, so it is the size token for a one-line value (28/32/36) and exactly one text line taller for a two-line one (48/52/56), always on the 4px rhythm, always aligned with the buttons and inputs beside it. Inside an InputGroup the group owns the row height: a SelectorOption folds onto one line and ellipsizes, and any taller node is cut off at the row.
indicatorPosition'start' | 'end' · default 'end'
Which logical edge of the option row carries a rendered selection mark. An empty mark consumes no space, so selected and unselected labels may shift or have different available width. end is the house convention shared with Typeahead and CommandPalette.
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.
isLoadingboolean · default false
Shows a loading spinner in the trigger.
xstyleStyleXStyles
StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value: not an inline style object like style={{}}.

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 the 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 one supplied option.

SelectorOption-rendered content · optional

Option content rendered with SelectorOption, either by the default renderer or by renderOption when it returns SelectorOption.

Bare caller-rendered option content · optional

Arbitrary content returned directly by renderOption without opting into SelectorOption.

Option selection indicator · optional

Resolved selection mark rendered for each option in its checked or unchecked state. Its layout space collapses when the resolved indicator draws nothing.

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-selector

Visual props: variant, size, status

States: disabled, readonly

astryx-selector-option
astryx-selector-option-row

Visual props: size

States: selected, disabled

astryx-selector-search
astryx-selector-section-heading
astryx-selector-empty-state
astryx-selector-clear-icon

Deprecated; use input-clear-icon.

astryx-selector-indicator-icon

States: state

astryx-selector-check
astryx-selector-popup