Skip to content
v2.5.0
English
Русский
Documentation

Select

Select is a compound component built from a root state container plus exported child parts for trigger, value, options, and native form participation.

Patterns

Searchable Select

When to use:

  • the option list is long
  • users need text filtering before making a choice
  • keeping the field compact still matters

Composition notes:

  • render a text input inside SelectListbox
  • disable built-in typeahead with typeahead={false}
  • filter the rendered SelectOption list from external state
  • pass the selected label to SelectValue as children: a filtered-out option is not rendered, so there is nothing to copy
  • handle Enter in the search field yourself (here it picks the first match), because arrow keys and Enter on the list only work while the trigger has focus

Trade-offs:

  • improves discoverability for long lists
  • increases keyboard complexity compared with a plain select
searchable-select.tsx · 109 lines
import {
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
  TextField,
  TextFieldInput,

Clearable Controlled Select

When to use:

  • the selected value can be reset
  • the field must sync with URL state, filters, or external form logic

Composition notes:

  • store selection in React state
  • pass value and onChangeValue
  • provide a separate clear action that sets null, not undefined

Trade-offs:

  • keeps state explicit and predictable
  • adds parent-state complexity compared with uncontrolled usage
controlled-value-with-clear-action.tsx · 56 lines
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

Select Inside Overlay

When to use:

  • a modal, drawer, or sheet contains a single-choice field
  • the listbox must coexist with another overlay system

Composition notes:

  • render Select normally inside the overlay content
  • no portal is needed: the listbox renders in the top layer above the overlay
  • retest focus restoration and dismissal in the real overlay

Trade-offs:

  • preserves normal field behavior inside overlays
  • requires more integration testing than isolated component usage
select-inside-dialog.tsx · 80 lines
import type { CSSProperties, FormEvent } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Label,
  Select,

Inline Sentence Select

When to use:

  • the control belongs inside readable sentence-like copy
  • the choice is lightweight and should feel embedded

Composition notes:

  • use variant='inline'
  • surround the field with meaningful text
  • avoid using inline variant without context
  • give the trigger its own aria-label: the sentence around it is not a label

Trade-offs:

  • feels lightweight and embedded
  • depends on surrounding copy for clarity and labeling
inline-variant-inside-sentence-like-ui.tsx · 31 lines
import {
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

export default function InlineVariantInsideSentenceLikeUi() {

Rich Option Content

When to use:

  • options need iconography or secondary copy
  • visual scanning matters more than minimal row density

Composition notes:

  • keep the value scalar
  • make the whole option row the selectable unit
  • do not split the option into multiple interactive controls
  • pass label so typeahead matches the name, and a short SelectValue child so the trigger does not copy the whole row
  • options have a fixed height, so multi-line content needs height: 'auto' on the option

Trade-offs:

  • improves scanability for complex choices
  • increases row height and semantic complexity
rich-option-content.tsx · 54 lines
import {
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

Native Form Select

When to use:

  • the selected value should be submitted through normal form mechanisms
  • the integration relies on FormData

Composition notes:

  • include SelectHiddenSelect
  • keep the select inside the form boundary

Trade-offs:

  • improves native form interoperability
  • adds one more required part to the composition
native-form-participation.tsx · 68 lines
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

Notes:

  • without SelectHiddenSelect, the control may still render correctly but the native form contract is weaker

Custom Icon

When to use:

  • the trigger icon should hint at the kind of value, not only at the list
  • the default chevron does not fit the surrounding UI

Composition notes:

  • pass your icon as children of SelectIcon; they replace the default chevron
  • keep SelectIcon inside SelectCombobox, after SelectValue

Trade-offs:

  • adds meaning to the trigger at a glance
  • a non-chevron icon makes it less obvious that the field opens a list
custom-icon.tsx · 27 lines
import {
  Select,
  SelectCombobox,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
} from '@vega-ui/react';
import { GlobeIcon } from '@vega-ui/icons';

export default function CustomIcon() {
  return (

Comparison

Quick Decision Rule

Choose Select if all of the following are true:

  • the interaction is single-choice
  • the value is string or number
  • the option set is better hidden than always visible
  • native form participation or field semantics matter

Choose another control if visibility, binary state, or richer action content is more important than compact field behavior.

Select vs SegmentedControl

Use Select when:

  • there are many options
  • space is constrained
  • options do not need to stay visible all the time

Use SegmentedControl when:

  • there are only a few options
  • users benefit from seeing all choices at once
  • switching between options should feel immediate

Trade-off:

  • Select saves space but hides options
  • SegmentedControl improves discoverability but consumes layout width

Select vs Radio

Use Select when:

  • the option list is long
  • the field belongs to a dense form
  • the control should stay compact

Use Radio when:

  • the option set is short
  • users should compare all available choices directly
  • explicit single-choice visibility is more important than compactness

Trade-off:

  • Select is denser
  • Radio is clearer for comparison

Select vs Checkbox

Use Select when:

  • users must choose exactly one option
  • the value should behave like a scalar field in a form

Use Checkbox when:

  • the interaction is binary
  • each control represents an independent yes or no choice

Do not replace multi-checkbox groups with a single Select unless the product model is truly single-choice.

Select vs Switch

Use Select when:

  • users are choosing from several values
  • the selected value should persist as one field

Use Switch when:

  • the interaction is immediate on or off state
  • the setting changes directly rather than choosing from a list

Trade-off:

  • Select is for enumerated choice
  • Switch is for binary state

Select vs Popover

Use Select when:

  • the main job is choosing a value
  • keyboard option navigation and value semantics are required

Use Popover when:

  • the content is not primarily a single-choice field
  • the overlay contains actions, help text, or custom UI rather than listbox semantics

Choose This Component When

  • the interaction is single-choice
  • the value should behave like one compact field
  • the option set is long enough that always-visible controls would add noise
  • form participation or dense layout matters

Do Not Choose This Component When

  • the user benefits from seeing all options at once
  • the interaction is binary rather than enumerated
  • the overlay content is not actually a field
  • the value model depends on object payloads instead of scalar keys

Troubleshooting

Selected Value Is Not Submitted With The Form

Symptom

The control renders and updates visually, but the selected value is missing from native form submission.

Likely Cause

SelectHiddenSelect is missing from the composition.

How To Verify

  • submit the surrounding form
  • inspect the resulting FormData
  • confirm that the expected field name or value is absent

Fix

Render SelectHiddenSelect with a name inside the root Select.

native-form-participation.tsx · 68 lines
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

Keyboard Users Cannot Pick A Filtered Option

Symptom

A search field inside SelectListbox filters the options, but after typing there is no way to choose one without a mouse.

Likely Cause

Arrow keys and Enter move through the list only while the trigger has focus. Once the search field takes the focus, the select does not handle its keys, and Tab moves the focus out of the list, which closes it.

How To Verify

  • open the select and type into the search field
  • press arrow keys and Enter
  • check whether any option gets selected

Fix

Handle Enter in the search field yourself: pick the first match, close the list and return focus to the trigger. Pass the selected label to SelectValue as children, because a filtered-out option is not rendered.

searchable-select.tsx · 109 lines
import {
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
  TextField,
  TextFieldInput,

The Select Works Visually But Keyboard Behavior Feels Broken In A Dialog

Symptom

The select opens, but focus movement, close behavior, or keyboard navigation feels inconsistent inside an overlay.

Likely Cause

The composed overlay behavior was validated in isolation but not retested inside Dialog, Drawer, or Sheet.

How To Verify

  • render the exact integration inside the real overlay
  • test open, close, focus return, and option navigation from the keyboard

Fix

  • keep the standard Select composition intact
  • retest with the real overlay stack
  • avoid custom trigger or option structures that interfere with focus flow

The Placeholder Never Appears

Symptom

placeholder is configured, but the trigger always shows some other content.

Likely Cause

An initial value is already selected, or custom SelectValue children always render content.

How To Verify

  • remove value and defaultValue
  • remove custom children from SelectValue
  • check whether the placeholder becomes visible

Fix

  • use placeholder only when no selected value is present
  • ensure custom value rendering respects the empty state

Clearing The Value Does Not Update The UI

Symptom

The parent clears the value logically, but the UI still shows the old selection.

Likely Cause

The parent sets the value to undefined. In Select, undefined means uncontrolled, so the component falls back to its own internal state, which still holds an old selection; null is the controlled empty value.

How To Verify

  • inspect the current value prop
  • confirm whether onChangeValue updates the same source of truth

Fix

Clear the controlled value with null and convert the undefined that onChangeValue can report: onChangeValue={(next) => setValue(next ?? null)}.

controlled-value-with-clear-action.tsx · 56 lines
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

Options Render Correctly But Selection Does Not Change

Symptom

Options are visible and clickable, but the selected value does not update as expected.

Likely Cause

SelectOption values do not match the expected scalar value model, or custom option composition broke the selectable row.

How To Verify

  • confirm each SelectOption has a string or number value
  • check whether the whole option row remains the selectable unit

Fix

  • keep option values scalar
  • avoid splitting one option into multiple competing interactive elements

Read-Only And Disabled Behave Differently Than Expected

Symptom

The control state looks right visually, but focusability or interaction does not match product expectations.

Likely Cause

readOnly and disabled were treated as visual variants rather than behavior variants.

How To Verify

  • test focusability and interaction in both states
  • confirm whether the control should still receive focus

Fix

  • use disabled when interaction must stop entirely
  • use readOnly when the field should remain focusable but immutable

Object Values Cause Type Or Behavior Problems

Symptom

Type errors, unstable selection, or mismatched values appear when passing full objects as option values.

Likely Cause

The public API is built for string and number values, not object payloads.

How To Verify

  • inspect the value passed to Select
  • confirm whether SelectOption value={...} is scalar

Fix

Store a scalar key in Select and resolve the full object in the parent layer.

The Select Looks Correct But The Composition Still Feels Fragile

Symptom

The component appears to work, but small customization changes keep breaking behavior.

Likely Cause

Too much behavior was pushed into custom wrappers instead of the shipped parts.

How To Verify

  • compare the current composition to the standard layout in index.md and anatomy.md
  • remove custom wrappers incrementally

Fix

  • return to the standard composition first
  • reintroduce custom wrappers only where the built-in parts are insufficient
  • use useSelectContext only for advanced cases