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
| Name | Type | Description |
|---|---|---|
value Required | string | Controlled value. |
onChange | (event: ChangeEvent<HTMLSelectElement>) => void | Native change handler. Optional when onValueChange is enough. |
onValueChange | (value: string) => void | Optional value callback. |
children | React.ReactNode | Native <option> and <optgroup> elements. |
className | string | Optional class on the select. |