Controls
Every form control in the library, grouped by what it captures. A table index — start here to find the right one.
Every control goes inside a Field the same way, so there is no per-control API
to learn. This page is the index: find the control you need by what it captures, then follow the
link for its own page.
The naming rule that explains half of these. A kebab-case file is the vendored
primitive — the compound with all its parts exposed. A PascalCase one is our pre-assembled
convenience, built on it: FacetFilter imports listbox and popover, FieldArray imports
field. They are not competitors; one is made of the other. Default to the PascalCase one, and
drop to the primitive when you need to rearrange the parts. The rule in full is on
Philosophy.
Text
| Control | What it is | Reach for it when |
|---|---|---|
Input | A single-line text input. | The everyday field. |
Input type="number" | The same input, numeric, with a decimal keypad on mobile. No steppers — see NumberInput for those. | A number typed straight in: a price, an amount. |
Textarea | Multi-line text. | A description, a note, free text. |
InputGroup | An input flanked by addons — a prefix, a unit, a button. | The value needs an affix or an inline action. |
Editable | Edit-in-place: a preview that swaps for an input on click. | The value is normally read, and editing it is the exception. |
Choice
| Control | What it is | Many | Reach for it when |
|---|---|---|---|
NativeSelect | The browser's own <select> element, styled to the input scale. | no — it is the OS picker | Few options and plain text: it costs no JavaScript, and on mobile it opens the platform picker. |
Select | A listbox in a popover, driven by a collection. | multiple | The options need to look like more than plain text: icons, descriptions, groups. |
Listbox | That same listbox with no popover of its own — you place it. | selectionMode | The list must live somewhere Select cannot put it: inline in a panel, or in a popover you own. |
Combobox | A text input plus that same listbox — typing filters the collection. | multiple | The list is long enough that typing beats scrolling. |
FacetFilter | A Listbox in a popover behind a trigger that counts what is picked. | multiple | Narrowing a table or a dashboard, where the options are data and carry counts. |
TagsInput | A list of removable tokens with no option collection — the user invents the values. | always | The values do not exist beforehand: free-form tags, keywords, addresses. |
RadioGroup | A few options, all visible at once. | no | One of a few, no popover. |
RadioGroupCard | That same item styled as a card, with room for an icon, a description or a preview. | no | One of a few, where the options need explaining. |
SegmentGroup | 2–4 options as a compact inline switch. Radio semantics, segmented look. | no | Switching a view in place. |
Rule of thumb: under ~7 options, NativeSelect; over ~15, Combobox, because that is where
scrolling starts costing more than typing; Select in between, when the options need to look
like more than plain text. Multiple selection is not the deciding axis — Select and
Combobox both take multiple, and TagsInput is multiple by nature. Typing is.
Autocomplete is not a control here. It is the presentation of a Combobox with no trigger
button — showTrigger={false} — so it opens on type rather than on click. Same machine, same
props, one page: Combobox. A further face of that same
machine, Command, is not a form control at all: what it captures is an
action, so it is filed under actions.
A menu is a command; a listbox is a value
Half the "which control?" questions here are one question wearing several hats, and the answer is
an ARIA role, not a look. Two surfaces open a popover over a list, and they are not
interchangeable. role="menu"
(Menu, MenuItem, MenuCheckboxItem) is a list of commands: you
press one, something happens, the surface closes. It is not anybody's data. role="listbox"
(inside Select and Combobox) is a value: it
lives in state, and the popover is only its editor.
The test, in one line: if closing the surface leaves state, it is a listbox; if it leaves only an effect, it is a menu.
That is the only axis that matters. The rest of the Choice table is two orthogonal questions on top of it — can you type to filter? and how many can you pick? — which is why these controls look alike and are not.
Command is the honest exception, and reading it clarifies the rule
rather than breaking it: a palette runs commands yet is built on a combobox, because the role
follows the interaction, not the payload. Type to filter, arrow to highlight, enter to run is
combobox behaviour, and a menu cannot filter at all. So: a value needs a listbox; a command needs
a menu unless it needs to be searched.
The rule bites hardest on filters, because a filter is a value while the
popover-with-checkboxes idiom looks like a menu. Getting the role wrong costs more than purity: a
menuitemcheckbox list cannot announce "2 of 5 selected", and a menu has nowhere to grow a search
field the day the list has 200 values.
Boolean
| Control | What it is | Reach for it when |
|---|---|---|
Switch | Reads as on/off. | A setting that takes effect immediately. |
Checkbox | Reads as included/excluded, with a third indeterminate state. | A value submitted with the form. |
CheckboxGroup | One value array across several checkboxes. | Related checkboxes you submit together. |
Value
| Control | What it is | Reach for it when |
|---|---|---|
NumberInput | A spinbutton: stepper triggers, arrow keys, drag-to-scrub. | A bounded quantity the user nudges — a count, a page size. |
Slider | A value or range on a track. | A bounded number where the range matters more than the digit. |
Rating | A row of stars on a fixed scale. | A score out of five. |
DatePicker | A date input with a calendar popover. Takes Ark's DateValue; the ISO-string adapter is two functions at your seam. | Picking a date in a form. |
Calendar | The same machine inline, so the month grid is always visible. | A calendar that is furniture, not a form value. |
ColorPicker | Area, hue and hex, with preset swatches. | Picking a colour. |
A row of colours the user is not editing is not a control at all: when the colour merely
pictures a value that has a name, reach for Swatch, which is
aria-hidden and never focusable.
Files
| Control | What it is | Reach for it when |
|---|---|---|
FileUpload | A dropzone plus the list of what was picked, with previews. | The value is a file, not text. |
Credentials
| Control | What it is | Reach for it when |
|---|---|---|
PasswordInput | A password input with a reveal toggle. | Entering a password — sign-in, sign-up. |
PasswordInput + hasStoredValue | The same input in stored-credential mode: renders empty when a secret exists, and submitting empty means "keep it". | Editing a credential that already lives server-side. |
PinInput | One cell per character; a pasted code spreads across them. | A one-time password or verification code. |
Repeating
| Control | What it is | Reach for it when |
|---|---|---|
FieldArray | N rows with add and remove, stateless — the caller owns the values. | A field the user can have several of. |
FieldArray's rowKey must be stable across a commit. If a row's key changes when its
value is first saved, React remounts the input and the user loses focus mid-typing — do not
"simplify" it to the array index.
AI-assisted
These take suggestions from a model, and the library never calls one — you pass a stream. See AI-assisted fields for the shared contract.
| Control | What it is | Reach for it when |
|---|---|---|
SuggestRoot + SuggestMark + SuggestList | A ✨ inside a field that streams candidates into a strip underneath it. | The user is choosing a value and needs options they did not think of. |
CompleteRoot + CompleteGhost | A field that completes inline as you type, Tab to accept. | The user is writing prose and a streamed continuation helps. |
Every one of these sets its state on the Field, never on the control — see
Building a form, then Validation for
how errors are shown.
RadioGroupProps is exported, so a wrapper can take the same props without restating them.