Field.Select

Field.Select is the native <select> inside a Field. It is not a JavaScript listbox.

On this page

When the browser supports appearance: base-select, the button and its picker are drawn from field and option tokens. Elsewhere the browser's own picker shows, with the same value and the same keyboard. Children are <option> or <optgroup> elements. A Label.Root htmlFor names the select like any other field.

const id = useId();

<Field.Group>
  <Label.Root htmlFor={id}>Size</Label.Root>
  <Field.Root id={id}>
    <Field.Select value={size} onChange={(event) => setSize(event.target.value)}>
      <option value="s">Small</option>
      <option value="m">Medium</option>
    </Field.Select>
  </Field.Root>
</Field.Group>

Icons

Field.Select draws the chevron glyph from a GlyphsProvider over the browser's picker icon when the browser supports ::picker-icon and the select is the field's last part; otherwise, and without a provider, the browser's icon shows. The check mark beside a selected <option> is the browser's own.

import { ChevronDown } from 'lucide-react';

<GlyphsProvider glyphs={{ chevron: <ChevronDown /> }}>
  <Field.Root aria-label="Plan">
    <Field.Select value={plan} onValueChange={setPlan}>
      …
    </Field.Select>
  </Field.Root>
</GlyphsProvider>

Accessible composition

Todae handles: The select takes id, name, aria-label, aria-labelledby and aria-describedby from Field.Root, and native disabled and aria-invalid from the Root or a surrounding Fieldset. A mouse press elsewhere in the field focuses the select and, where the browser allows, opens its picker. The chevron glyph is aria-hidden. The browser handles the keyboard and the picker.

You supply: A name, through a Label whose htmlFor is the Root id or aria-label / aria-labelledby on the Root. onChange or onValueChange; without either, the select is read-only.

Watch for: multiple and size are not props of Field.Select; for several values use Checkbox or Field.ChipInput.

When to use

Field.Select

When: Pick one value from a short, fixed list inside a form. The control is the native <select> and its keyboard, not a JavaScript listbox.

When not: Filtering or searching a long list → Combobox recipe. Several values → Checkbox or Field.ChipInput. Commands rather than values → Menu.

Props

Field.Select props
NameTypeDescription
value Requiredstring

Controlled value.

onChange(event: ChangeEvent<HTMLSelectElement>) => void

Native change handler. Optional when onValueChange is enough.

onValueChange(value: string) => void

Optional value callback.

childrenReact.ReactNode

Native <option> and <optgroup> elements.

classNamestring

Optional class on the select.