VayuUI

Select

A flexible dropdown select with single, multi, async search, and creatable modes using compound components.

Installation

npx vayu-ui-cli@latest add select

Usage

Advanced Select Component

With Async Search, Createable Options & Compound Components

Basic Single Select

Static

Selected: apple

Multi Select

Tags

Selected: ["react","nextjs"]

Async Search

Search users from simulated API

API

Selected: none

Createable Select

Create new options via API

New!

Selected: none

Createable Multi Select

New!

Selected: ["typescript"]

Async + Createable

Search products and create if not found

Combined

Selected: none

Error State

This field is required

{/* Single Select */}
<Select.Root value={value} onValueChange={setValue} label="Select Fruit">
  <Select.Trigger placeholder="Choose a fruit..." />
  <Select.Content>
    <Select.List>
      <Select.Item value="apple">Apple</Select.Item>
      <Select.Item value="banana">Banana</Select.Item>
      <Select.Item value="cherry">Cherry</Select.Item>
    </Select.List>
    <Select.NotFound />
  </Select.Content>
</Select.Root>

{/* Multi Select */}
<Select.Root value={multiValue} onValueChange={setMultiValue} label="Technologies" multiple>
  <Select.Trigger placeholder="Add technologies..." />
  <Select.Content>
    <Select.List>
      <Select.Item value="react">React</Select.Item>
      <Select.Item value="nextjs">Next.js</Select.Item>
      <Select.Item value="vue">Vue</Select.Item>
      <Select.Item value="svelte">Svelte</Select.Item>
    </Select.List>
    <Select.NotFound />
  </Select.Content>
</Select.Root>

{/* Async Search */}
<Select.Root
  value={asyncValue}
  onValueChange={setAsyncValue}
  label="Search Users"
  onSearch={handleSearch}
  searchDebounce={300}
  minSearchLength={1}
>
  <Select.Trigger placeholder="Type to search users..." showSearchIcon />
  <Select.Content>
    <Select.Loading />
    <Select.SearchHint />
    <Select.AsyncOptions />
    <Select.NotFound>No users found</Select.NotFound>
  </Select.Content>
</Select.Root>

{/* Creatable */}
<Select.Root
  value={value}
  onValueChange={setValue}
  label="Select or Create Skill"
  creatable
  onCreateOption={handleCreate}
  validateCreate={(val) => val.length >= 2 || 'Too short'}
  createText="Add Skill"
>
  <Select.Trigger placeholder="Select or create a skill..." />
  <Select.Content>
    <Select.List>
      <Select.Item value="frontend">Frontend</Select.Item>
      <Select.Item value="backend">Backend</Select.Item>
    </Select.List>
    <Select.CreateButton />
    <Select.NotFound />
    <Select.Error type="create" />
  </Select.Content>
</Select.Root>

{/* Error State */}
<Select.Root label="Required Field" error="This field is required">
  <Select.Trigger placeholder="Select an option..." />
  <Select.Content>
    <Select.List>
      <Select.Item value="1">Option 1</Select.Item>
      <Select.Item value="2">Option 2</Select.Item>
    </Select.List>
  </Select.Content>
</Select.Root>

Anatomy

import { Select } from 'vayu-ui';

{
  /* Basic Structure */
}
<Select.Root value={value} onValueChange={setValue} label="Label">
  <Select.Trigger placeholder="Choose..." />
  <Select.Content>
    <Select.List>
      <Select.Item value="opt1">Option 1</Select.Item>
      <Select.Item value="opt2">Option 2</Select.Item>
    </Select.List>
    <Select.Loading />
    <Select.SearchHint />
    <Select.AsyncOptions />
    <Select.CreateButton />
    <Select.NotFound>No results</Select.NotFound>
    <Select.Error type="search" />
    <Select.Footer>Footer content</Select.Footer>
  </Select.Content>
</Select.Root>;
  • Select.Root — Root container. Manages selection state, search, and provides context to children.
  • Select.Trigger — Button with search input and multi-value chips. Toggles the dropdown open/close.
  • Select.Content — Portal-based dropdown with automatic positioning above or below the trigger.
  • Select.List — Hybrid renderer for static items and async options. Wraps Select.Item elements.
  • Select.Item — Individual selectable option with check icon for selected state.
  • Select.AsyncOptions — Renders options returned from async search results.
  • Select.CreateButton — Button to create a new option when creatable is enabled.
  • Select.Loading — Loading spinner shown during async search.
  • Select.SearchHint — Hint text shown when search input is shorter than minSearchLength.
  • Select.NotFound — Empty state displayed when no options match the search.
  • Select.Error — Error message for failed search or create operations.
  • Select.Footer — Optional footer content at the bottom of the dropdown.

Accessibility

  • Keyboard Support:
    • Enter / Space — Select the focused option or open the dropdown.
    • ArrowDown — Move focus to the next option. Wraps to first. Opens dropdown if closed.
    • ArrowUp — Move focus to the previous option. Wraps to last. Opens dropdown if closed.
    • Escape — Close the dropdown and return focus to the trigger input.
    • Tab — Move focus between the trigger input and options. Shift+Tab for reverse.
    • Backspace — Remove the last selected value in multi-select mode when search input is empty.
  • ARIA Attributes:
    • role="listbox" on Select.Content.
    • role="option" on each Select.Item and Select.CreateButton.
    • aria-selected on Select.Item reflects the selected state.
    • data-disabled on Select.Item when the option is disabled.
    • Label linked to input via htmlFor and generated id.
  • Focus Behavior:
    • Clicking the trigger focuses the search input and opens the dropdown.
    • Arrow keys move focus between options within the listbox.
    • Closing the dropdown returns focus to the trigger input.
    • Programmatic focus is tracked to prevent unwanted dropdown open on refocus.

Screen reader behavior

When a user focuses the select trigger, the screen reader announces the label and the currently selected value or placeholder. When the dropdown opens, the listbox role is announced along with the number of available options. Each option is announced with its label and selected state via aria-selected. Disabled options are announced as unavailable. When no results are found, the Select.NotFound content is announced. In multi-select mode, selecting or removing values is announced. The Select.Loading and Select.SearchHint states are announced when they appear during async search.

Component Folder Structure

Select/
├── Select.tsx              # Root component with context provider and state management
├── SelectTrigger.tsx       # Trigger with search input and multi-value chips
├── SelectContent.tsx       # Portal-based dropdown with auto-positioning
├── Selectitem.tsx          # Individual selectable option
├── SelectCreateButton.tsx  # Creatable option button with validation
├── SelectOptionList.tsx    # Static and async list renderers
├── SelectStates.tsx        # Loading, NotFound, SearchHint, Error, Footer states
├── types.ts                # TypeScript type definitions
├── index.ts                # Re-exports all components and types
└── README.md               # Component usage reference

Props

Select.Root

PropTypeDefaultDescription
childrenReactNodeSelect subcomponents.
valueSelectValueControlled value.
defaultValueSelectValueUncontrolled initial value.
onValueChange(value: SelectValue) => voidCallback when value changes.
labelstringAccessible label for the select.
errorstringError message displayed below the trigger.
multiplebooleanfalseEnable multi-select mode.
classNamestringAdditional CSS classes.
onSearch(searchValue: string) => Promise<OptionData[]>Async search handler. Enables async mode.
searchDebouncenumber300Debounce delay in ms for async search.
minSearchLengthnumber1Minimum characters before triggering search.
isLoadingbooleanExternal loading state override.
creatablebooleanfalseEnable option creation.
onCreateOption(inputValue: string) => Promise<OptionData | null>Handler to create a new option.
isCreatingbooleanExternal creating state override.
createTextstring"Create option"Text for the create button.
validateCreate(inputValue: string) => boolean | stringValidation function. Returns true or error text.

Select.Trigger

PropTypeDefaultDescription
placeholderstringPlaceholder text for the input.
showSearchIconbooleanfalseShow a search icon in the trigger.
classNamestringAdditional CSS classes.

Select.Content

PropTypeDefaultDescription
childrenReactNodeDropdown content subcomponents.
classNamestringAdditional CSS classes.

Select.Item

PropTypeDefaultDescription
valuestring | numberUnique value. Required.
childrenReactNodeOption label content.
disabledbooleanfalseDisable selection.
classNamestringAdditional CSS classes.

Select.List

PropTypeDefaultDescription
childrenReactNodeSelect.Item elements for static options.
classNamestringAdditional CSS classes.

Select.AsyncOptions

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Select.CreateButton

PropTypeDefaultDescription
childrenReactNodeCustom button label.
classNamestringAdditional CSS classes.

Select.Loading

PropTypeDefaultDescription
childrenReactNode"Searching..."Custom loading text.
classNamestringAdditional CSS classes.

Select.NotFound

PropTypeDefaultDescription
childrenReactNode"No results found"Custom empty text.
classNamestringAdditional CSS classes.

Select.SearchHint

PropTypeDefaultDescription
childrenReactNodeCustom hint text.
classNamestringAdditional CSS classes.

Select.Error

PropTypeDefaultDescription
type'search' | 'create''search'Which error state to display.
childrenReactNodeCustom error content.
classNamestringAdditional CSS classes.

Select.Footer

PropTypeDefaultDescription
childrenReactNodeFooter content.
classNamestringAdditional CSS classes.

On this page