<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
| Prop | Type | Default | |
|---|---|---|---|
onValueChange | (value: string | null) => void | required | |
options | readonly SelectFieldOption[] | required | |
value | string | null | required | Null while nothing is picked, or when nullOption is chosen. |
className | string | — | |
createLabel | ((query: string) => string) | — | What the create row reads as. Defaults to Create "<query>". |
disabled | boolean | — | |
emptyHint | string | — | 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. |
id | string | — | |
invalid | boolean | — | |
isBlurIntoOwnPopup | ((event: FocusEvent<HTMLElement, Element>) => boolean) | — | Blur into the control's own popup, which must not count as leaving it. |
loading | boolean | — | 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. |
name | string | — | |
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) | — | |
placeholder | string | — | |
searchValue | string | — | 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. |
size | SelectFieldSize | — | |
testId | string | — | 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.