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
Item