Skip to main content

Design system components

Select

Single-choice field that opens the DS option list (a select-only combobox), with labelled options, statuses, label-row actions and size tokens.

Status: stable. Group: form. Markdown version: /design-system/components/select.md

When to use

Select is the single-choice field. It renders Combobox's select-only combobox inside FieldShell, so it opens the same DS option list as every other dropdown on the site, never the operating system's popup. Feed it an options array or option children (an empty-value option becomes the placeholder) and read changes via onValueChange; pass name to submit with a native form.

Use Select for five or more exclusive options, or when space is tight; RadioGroup is better when all options should stay visible.

When not to use

  • Do not ship a Select with value and defaultValue both set; value wins and a dev warning fires.
  • Do not use Select for a freeform value with suggestions; use TextInput.
  • Do not render a native <select> beside or inside it; every DS dropdown opens the DS list.

Look-alikes

  • RadioGroup: Use Select for five or more exclusive options, or when space is tight; RadioGroup is better when all options should stay visible.
  • SegmentedControl: Do not overflow it; if the options do not fit on one line, use Select.
  • SelectOption: Use the Select options array for ordinary flat data; use SelectOption children for explicit declarative composition.
  • TextInput: Do not use Select for a freeform value with suggestions; use TextInput.

Props

Select props
PropTypeDefaultDescriptionRequired
labelstringnoneLabel text displayed above the select dropdown.Yes
optionsSelectOptionItem[][]Option objects with value, label, and optional disabled; children override this.No
helperTextstringnoneSupporting text under the control; always rendered, below error when both are set.No
errorstringnoneError message under the control; sets aria-invalid and aria-describedby.No
statusFieldStatusnoneSemantic state: colours the border and styles statusMessage. error overrides it.No
statusMessagestringnoneMessage under the control for status, above the helper line.No
actionsFieldAction[]noneIcon buttons at the end of the label row: each runs an operation or opens a menu.No
size"sm" | "md" | "lg" | "S" | "M" | "L"mdSize variant for select.No
onValueChange(value: string) => voidnoneValue change handler (recommended)No
disabledbooleanfalseDisables the select.No
onChange(value: string) => voidnoneNo description in the contract. Deprecated: Deprecated.No
childrenReact.ReactNodenone<option> or SelectOption children; an option with value "" becomes the placeholder.No
valuestringnoneSelected value (controlled); "" shows the placeholder.No
defaultValuestringnoneInitial value when uncontrolled.No
requiredbooleanfalseMarks the field required.No
idstringnoneExplicit control id (wires the label); auto-generated when omitted.No
namestringnoneSubmits the value with a surrounding form, through a hidden input.No
classNamestringnoneClasses on the field wrapper.No

Variants

  • Available sizes: sm, md, lg, S, M, L (default md). Forwarded to CSS module size classes.

Invalid combinations

  • MUST NOT: value and defaultValue set together. value makes the component controlled and defaultValue uncontrolled; pass one, not both.

Keyboard

Select keyboard behaviour
KeyBehaviour
TabMoves focus to and from the field and closes an open list. Focus moving to any element outside the field also closes it.
ArrowDownSkips disabled options.
ArrowUpSkips disabled options.
HomeFirst enabled option.
EndLast enabled option.
EnterActivates the focused control. (standard)
SpaceActivates or toggles the focused control. (standard)
EscapeCloses or dismisses the open layer. (standard)
GeneralType-ahead.

Rows marked (standard) name a key the contract lists without a description; the behaviour shown is the usual one for that key.

Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

Select rules
LevelRuleHow it is checked
MUST NOTvalue and defaultValue set together. value makes the component controlled and defaultValue uncontrolled; pass one, not both.Checked by the MCP tool validate_component_usage.
MUSTARIA: Combobox named by the associated label.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Listbox with aria-selected and aria-activedescendant.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Status message and helper linked with aria-describedby; error announced as an alert.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTRequired stories produce no axe violations at the configured severity.Automated by npm run test:stories:matrix:ci. WCAG 2.5.8.
MUSTThe accessibility tree (role, name, state) matches the committed snapshot for every required story.Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2.
MUSTThe component remains legible and operable under a real browser forced-colors: active pass.Automated by npm run test:stories:hc.
MUSTEvery documented keyboard interaction works as specified.Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2.
MUSTA human completed an end-to-end accessibility review of this component.Manual review, last on 2026-11-04.
SHOULDLead with an empty placeholder option when there is no safe default, and validate it as unselected.Not stated in the contract.
SHOULD NOTDo not expect Select to filter a long list as the user types; like Combobox, whose engine it uses, it only moves the highlight to the first typeahead match.Not stated in the contract.
SHOULDPass name when the field submits with a native form; a hidden input carries the selected value.Not stated in the contract.
SHOULD NOTDo not silently preselect a consequential option (billing plans); make the user choose.Not stated in the contract.
SHOULDKeep option labels short; the list truncates nothing, so long labels wrap and slow scanning.Not stated in the contract.

Install and import

  • Install: npm install @digitaltableteur/react (version 0.9.1, restricted access on npm).
  • Import: import { Select } from "@digitaltableteur/react";
  • Entry point: @digitaltableteur/react/forms exports it too.
  • Styles: import @digitaltableteur/react/style.css once at the app boundary.
  • Also available as the custom element <dt-select>; see the custom elements manifest.