InputsReact
An always visible list of options where one value is selected, or several with multiple.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<Listbox.Root>
<Listbox.Item>
<Listbox.ItemIndicator />
<Listbox.ItemText />
</Listbox.Item>
</Listbox.Root>
// Not in this example: Listbox.Empty, Listbox.Group, Listbox.GroupLabel, Listbox.SeparatorExamples
Multiple
multiple keeps several values; the selected toppings are listed below.
Grouped
Group and GroupLabel add headings, and a Separator divides the groups.
Controlled
value and onValueChange keep the value in state; a button sets it from outside, and the readout shows the last details.reason (pointer or keyboard).
Horizontal
orientation="horizontal" makes a size picker that the left and right arrow keys move through; in a right-to-left layout they are mirrored.
Disabled
disabled on Root takes the whole list out of the tab order; disabled on an Item keeps it out of focus, ranges and typeahead.
Read Only
readOnly keeps the preselected value: focus and typeahead still move, presses do nothing.
Disallow Empty
disallowEmptySelection only has an effect with multiple: it keeps the last selected option from being released. A single listbox never deselects, like a native <select>.
Empty
Empty shows "No results" while there are no items, and the root gets data-empty; a button adds and removes the items.
Form
Inside Field and Form: name comes from the Field, required blocks the submit until a size is chosen, and the multiple listbox submits toppings=cheese&toppings=olives.
Object Values
Items hold objects: isItemEqualToValue matches the value by id, and itemToStringValue gives the id the form submits.
Highlight
highlightedValue and onHighlightChange report the option that holds focus, and null once focus leaves.
Virtualized
1,000 options, of which only the rows near the scroll position render; items and virtualized make Home, End, PageUp, PageDown, looping, typeahead and ranges work on all of them, and onItemHighlighted scrolls the target into view.
API reference
31 props across 8 parts, in React syntax. className, style, render are listed once, under Shared props.
EmptyListbox.Empty
Content shown while the listbox has no items. Renders nothing otherwise. It has no ARIA role, since only options and groups may sit inside a listbox. Renders a <div>.
GroupListbox.Group
Groups related listbox items with the corresponding label. Renders a <div>.
GroupLabelListbox.GroupLabel
An accessible label that is automatically associated with its parent group. Renders a <div>.
ItemListbox.Item
An individual option in the listbox. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
valueA unique value that identifies this item. | any | null |
labelOverrides the text that typeahead matches. | string | — |
indexThe zero-based position of the item in the full list. | number | — |
countThe size of the full list, for aria-setsize. | number | — |
disabledWhether the component should ignore user interaction. | boolean | false |
| Data attribute | Description |
|---|---|
data-selected | Present when the listbox item is selected. |
data-highlighted | Present when the listbox item is highlighted (holds focus). |
data-disabled | Present when the listbox item is disabled. |
ItemIndicatorListbox.ItemIndicator
Indicates whether the listbox item is selected. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
keepMountedWhether to keep the HTML element in the DOM when the item is not selected. | boolean | — |
| Data attribute | Description |
|---|---|
data-starting-style | Present when the indicator begins animating in. |
data-ending-style | Present when the indicator is animating out. |
ItemTextListbox.ItemText
A text label of the listbox item. Renders a <div>.
RootListbox.Root
Groups all parts of an always visible list of options. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
valueThe value of the listbox. | ListboxInputValue<Value, Multiple> | null | — |
defaultValueThe uncontrolled value of the listbox when it's initially rendered. | ListboxInputValue<Value, Multiple> | null | — |
onValueChangeEvent handler called when the value changes. | ((value: ListboxOutputValue<Value, Multiple>, eventDetails: ListboxRootChangeEventDetails) => void) | — |
multipleWhether multiple items can be selected. | Multiple | false |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the selection. | boolean | false |
requiredWhether the user must choose a value before submitting a form. | boolean | false |
disallowEmptySelectionWhether the last selected item of a multiple listbox cannot be deselected, so the listbox | boolean | false |
highlightedValueThe value of the highlighted item (the one that holds focus). | Value | null | — |
onHighlightChangeEvent handler called when the highlighted item changes. | ((value: Value | null) => void) | — |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the hidden input. | string | — |
orientationThe orientation of the listbox. | Orientation | 'vertical' |
loopFocusWhether to loop keyboard focus back to the first item when the end of the list is reached | boolean | true |
isItemEqualToValueCustom comparison logic used to determine if an item value matches the current value. | ((itemValue: Value, value: Value) => boolean) | — |
itemToStringValueWhen the item values are objects, this function converts the object value to a string | ((itemValue: Value) => string) | — |
itemsEvery item of the list, mounted or not. | readonly Value[] | — |
virtualizedWhether the items are rendered by an external virtualizer, so only some of items are | boolean | false |
itemToStringLabelWhen the items are objects, converts an item to the text typeahead matches. | ((itemValue: Value) => string) | — |
isItemDisabledWhether an item of items is disabled, for items that are not mounted. | ((itemValue: Value) => boolean) | — |
onItemHighlightedVirtualized only: called when keyboard navigation (or highlightedValue) is about to move | ((itemValue: Value, eventDetails: ListboxRootHighlightEventDetails) => void) | — |
inputRefA ref to access the hidden input element. | React.Ref<HTMLInputElement> | — |
| Data attribute | Description |
|---|---|
data-disabled | Present when the listbox is disabled. |
data-readonly | Present when the listbox does not allow changing the selection. |
data-required | Present when the listbox is required. |
data-multiple | Present when the listbox allows multiple selected items. |
data-orientation | Indicates the orientation of the listbox. |
data-empty | Present when the listbox has no items. |
SeparatorListbox.Separator
A visual separator between items or groups. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
orientationThe orientation of the separator. | Orientation | 'horizontal' |
| Data attribute | Description |
|---|---|
data-orientation | Indicates the orientation of the separator. |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| ArrowDownArrowUpHomeEnd | Moves focus between the options without selecting (ArrowRight and ArrowLeft when horizontal) | Root |
| PageDownPageUp | Moves focus ten options and stops at the ends | Root |
| Moves focus to the next option whose text starts with what was typed, without selecting | Root | |
| EnterSpace | Selects the focused option; with multiple, toggles it | Item |
| ShiftArrowDownShiftArrowUp | With multiple, extends the selection from the last pressed option | Root |
| ShiftHomeShiftEnd | With multiple, extends the selection to the first or last option | Root |
| ShiftSpace | With multiple, selects the range from the last pressed option to the focused one | Root |
| ControlAMetaA | With multiple, selects every enabled option, or clears them when all are selected | Root |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Root | role="listbox", aria-multiselectable with multiple, aria-orientation | the options are read as one list, and whether several can be chosen |
| Root | aria-disabled, aria-readonly, aria-required | the list is read with its constraints; name it with aria-labelledby |
| Item | role="option", aria-selected, aria-disabled | each option is read with its selected and disabled state |
| Group | role="group", aria-labelledby to its GroupLabel | grouped options are read under their heading |
| GroupLabel | aria-hidden="true" | the heading is read once, as the group's name |
| Separator | role="presentation" | the divider is not read; only options and groups may sit in a listbox |
Focus order
- The listbox is one tab stop: the first selected option, else the first enabled one.
- Arrow keys,
Home,End,PageUpandPageDownmove focus between options and skip disabled ones; withloopFocusthe arrows wrap at the ends. Withvirtualized, these keys work onitems, not on the rows that are mounted. Tableaves the listbox.