Listbox

An accessible listbox with single and multiple selection, grouping, and typeahead.

Features

  • Single or multiple
  • Groups and nesting
  • Typeahead
  • Controlled or uncontrolled
  • Managed focus

Installation

npm install @ariaui/listbox

Examples

Three playgrounds from the legacy docs: a compact uncontrolled list with defaultValue, a controlled single-select with groups and a Listbox.Sub, and the same layout with selectionMode="multiple" plus a selection readout.

Styling uses semantic tokens via listboxExampleTokens.ts; checkmarks use @heroicons/react/24/solid. Controlled examples use exampleSource fences so hooks stay readable in the snippet.

Basic

Uncontrolled list with a default selected option.

Preview

No preview output yet.

live.tsxReady

Max visible items

Listbox with a fixed-height scroll region for a long option list.

Preview

No preview output yet.

live.tsxReady

Single selection with submenu

Grouped options with a nested submenu and controlled selection.

Preview

No preview output yet.

live.tsxReady

Multiple selection with submenu

Multi-select listbox with nested options and a live selection readout.

Preview

No preview output yet.

live.tsxReady

Anatomy

tsx
import * as Listbox from "@ariaui/listbox";

export default function Example() {
  return (
    <Listbox.Root>
      <Listbox.Label />
      <Listbox.Content>
        <Listbox.Viewport>
          <Listbox.Group>
            <Listbox.GroupLabel />
            <Listbox.Option />
          </Listbox.Group>
          <Listbox.Sub>
            <Listbox.SubTrigger />
            <Listbox.SubContent>
              <Listbox.Option />
            </Listbox.SubContent>
          </Listbox.Sub>
        </Listbox.Viewport>
      </Listbox.Content>
    </Listbox.Root>
  );
}

API Reference

Root

Context provider and state container for the listbox. Coordinates selection and active-option behavior.

PropTypeDefault
selectionMode
'single' | 'multiple''single'
value
string | string[]—
defaultValue
string | string[]—
onValueChange
(value: string | string[]) => void—

Label

Accessible label for the listbox. Referenced by Content via aria-labelledby.

Content

The focusable listbox surface. Contains options and handles keyboard navigation.

AttributeValues
rolelistbox
aria-labelledbyID of the Label element
aria-multiselectabletrue | false
aria-activedescendantID of the active option

Viewport

Optional inner wrapper that caps visible height and uses native overflow scrolling for long lists.

PropTypeDefault
maxVisibleItems*
number—
AttributeValues
[data-listbox-viewport]Empty attribute for styling/testing

Option

A selectable item in the listbox.

PropTypeDefault
value*
string—
disabled
booleanfalse
AttributeValues
roleoption
aria-selectedtrue | false
aria-disabledtrue when disabled
[data-active]true when focused/hovered

Group

A group of related options. Associates with GroupLabel via aria-labelledby.

AttributeValues
rolegroup
aria-labelledbyID of the GroupLabel

GroupLabel

Label for an option group.

Sub

Container for a submenu composed of SubTrigger and SubContent.

PropTypeDefault
offset
{ x: number; y: number }—

SubTrigger

Trigger that opens a submenu on hover or ArrowRight.

AttributeValues
[data-state]open | closed
[data-active]true when focused/hovered

SubContent

Submenu content surface containing nested options.

AttributeValues
[data-state]open | closed

Keyboard

ShortcutAction
↓Move focus to the next option. Wraps to first.
↑Move focus to the previous option. Wraps to last.
HomeMove focus to the first option.
EndMove focus to the last option.
EnterSelect the focused option. In multi-select, adds to the selection.
SpaceSelect the focused option. In multi-select, toggles selection.
→Open a submenu when a SubTrigger is focused and move focus to its first item.
←Close a submenu and return focus to its SubTrigger.
EscClose an open submenu and return focus to its SubTrigger.
A-Z/0-9Typeahead — jump to the next option whose label starts with the typed characters (case-insensitive, 500ms buffer).

Accessibility

The Listbox component implements the WAI-ARIA Listbox pattern using aria-activedescendant:

  • Content renders as role="listbox" with tabindex="0" and aria-activedescendant tracking the active option.
  • Option renders as role="option" with aria-selected reflecting the selection state.
  • aria-multiselectable="true" is applied when selectionMode="multiple".
  • Group renders as role="group" and is labelled by its GroupLabel.
  • DOM focus stays on the listbox container during keyboard navigation.
  • Disabled options expose aria-disabled="true" and cannot be selected.
Previous
Label