Skip to content

Forms

ICombobox ​

A select you can type into. It takes the same items as ISelect and filters them against the query. The field shows the selected option's label; the model holds its value.

A plain string is expanded to { label: value, value }, so a list of names needs no shaping.

Groups ​

An entry with its own items becomes a labelled run of options. A group disappears once nothing inside it matches — type cir and watch "Recent" go.

Sizes ​

Empty results ​

emptyText is the line shown when the query matches nothing. The empty slot replaces that markup entirely and receives the current query.

Create from the query ​

Set create to offer a row for whatever was typed when nothing matches. Choosing it emits create with the trimmed query and leaves the model alone — add the option and select it yourself.

Virtualized rows ​

For lists in the thousands, virtual renders only the rows on screen. The field below holds 5,000 options.

virtual cannot be combined with grouped items: groups are flattened and their labels dropped, with a warning in dev. estimateSize is the assumed row height in px, used to size the scrollbar before the rows are measured — set it when you have restyled rows to a different height.

Use virtual for lists in the thousands.

Invalid ​

Inside an IFormField the field passes its own validity down, so invalid rarely needs setting by hand.

Clearing ​

clearable turns the dropdown arrow into a clear button as soon as something is selected — the arrow is only useful while the field is empty, and two targets in the same corner of a 36px field is a mis-click waiting to happen.

Clearing empties the query, returns focus to the input and sets the model to null — or to [] when multiple.

Rename it for a non-English app with clearLabel, which is its accessible name.

Multiple ​

multiple lets the field hold several values. The model becomes an array and chosen rows keep their tick.

Acme Industries
Bolt Logistics

Each value is drawn as a removable chip inside the field, and the input stays a query box — so the labels are readable at a glance and there is still somewhere to type. Remove one with its cross, or with while the query is empty — the first press marks the last chip and the next removes it, as in ITagsInput. The field wraps and grows rather than clipping, the same trade ITagsInput makes.

The popup stays open as you tick rows, so a run of choices is one visit rather than one visit each.

Reach for ITagsInput instead when the values are free text rather than a fixed list, and for ICheckbox when there are only a handful of options and hiding them behind a popup buys nothing.

Props ​

PropTypeDefaultDescription
items(ComboboxItemOption | ComboboxItemGroup | string)[]—Options to render
placeholderstring—Shown while the field is empty
size'sm' | 'md' | 'lg''md'Control scale
invalidboolean—Red border and ring; inherited from IFormField when unset
idstring—Id for the input; IFormField supplies one
emptyTextstring'No results found.'Line shown when nothing matches
clearableboolean—Swap the arrow for a clear button once a value is set
clearLabelstring'Clear'Accessible name for that button
multipleboolean—Hold several values as chips; the model becomes an array
removeLabel(label: string) => stringRemove …Accessible name for a chip's remove button
createboolean—Offer a "create" row for an unmatched query
createLabel(query: string) => stringCreate "…"Label for that row
virtualboolean—Render only the rows in view; flattens groups
estimateSizenumber32Assumed row height in px, before rows are measured
overscannumber12Rows rendered beyond the viewport on each side
unstyledboolean—Drop built-in classes
classstring—Merged with the built-in classes
ui{ anchor?, input?, trigger?, clear?, tag?, tagText?, tagDelete?, content?, viewport?, item?, empty?, group?, groupLabel? }—Per-element class overrides

disabled, dir and the rest of Reka UI's ComboboxRoot props are forwarded.

Events ​

EventPayloadWhen
createquery: stringThe create row was chosen. The model is left alone

Reka UI's own ComboboxRoot events are re-emitted unchanged.

Slots ​

SlotPropsWhen to use it
empty{ query }Replace the no-results line with your own markup
create{ query }Replace the create row's label with your own markup
tag{ option, remove }Replace a chip's contents; call remove() to drop that value

Item shapes ​

ts
interface ComboboxItemOption {
  label: string
  value: string
  disabled?: boolean
}

interface ComboboxItemGroup {
  label: string
  items: (ComboboxItemOption | string)[]
}

Attributes you set land on the input, so aria-label and friends reach the control the reader types into.