SelectField

A select over a list of options, optionally one you can add to.

<SelectField
	options={[{ value: "design", label: "Designer" }, …]}
	value={role}
	onValueChange={setRole}
	placeholder="Pick a role"
/>

The form field built from ResponsiveSelect and Combobox, so a form does not re-wire either for the common case. It takes the options already resolved and labelled — where they come from, what to say while they load and when they fail stay with you. A closed field keeps ResponsiveSelect's native picker on phones.

A "none" row

The empty value is the placeholder and is never emitted. When "nothing" is a real choice — "All roles" in a filter, "No template" in a form — pass nullOption, and choosing it emits null.

value: "dev"

Creatable

onCreate lets the reader keep what they typed when nothing matches: one more row at the end of the list, rather than a trip to some other screen that loses whatever they were in the middle of. Typing needs a text input, so a creatable field renders a combobox styled as the trigger it replaces, at the same heights.

Matching ignores case and accents, so "etude" finds "Étude". The create row is never offered for a label that already exists. For options fetched per query, pass searchValue/onSearchChange and the field stops narrowing on its own; loading shows a spinner instead of announcing an empty list, and emptyHint says what an empty catalog means.

API

PropTypeDefault
onValueChange(value: string | null) => voidrequired
optionsreadonly SelectFieldOption[]required
valuestring | nullrequiredNull while nothing is picked, or when nullOption is chosen.
classNamestring—
createLabel((query: string) => string)—What the create row reads as. Defaults to Create "<query>".
disabledboolean—
emptyHintstring—What the open list says when it has no row to offer — an empty catalog with nothing typed yet. Without it the popup stays hidden instead of opening as a bare box. Creatable only: a closed select over no options is the caller's empty state, not the list's.
idstring—
invalidboolean—
isBlurIntoOwnPopup((event: FocusEvent<HTMLElement, Element>) => boolean)—Blur into the control's own popup, which must not count as leaving it.
loadingboolean—True while the options are being (re)fetched. The open list swaps the empty hint for a spinner, so a list still crossing the network is never announced as empty. Creatable only, like emptyHint.
namestring—
nullOption{ readonly label: string; }—A deliberate "no option" row ("None", "All categories"). Choosing it emits null. Without it, the empty value stays the placeholder and is never emitted. Closed select only.
onBlur((event: FocusEvent<HTMLElement, Element>) => void)—
onCreate((query: string) => void)—Lets the reader keep what they typed when no option matches, turning the select into a field you can type into. Called with the typed text. A list is never finished, and the reader who notices is mid-sentence: sending them off to create the option somewhere else loses whatever they were in the middle of. Omit it and the list stays a closed set, which is what most lists are.
onSearchChange((query: string) => void)—
placeholderstring—
searchValuestring—The typed text, when the caller narrows the options itself — a list coming from a server has already answered the query. Left out, the control narrows the options it was handed.
sizeSelectFieldSize—
testIdstring—Lands on the trigger or the input as data-testid.

Plus the DOM props of the element it renders. Source: select-field/components/select-field.tsx.