Listbox

Shows an inline, keyboard-navigable list of options with no trigger of its own.

import { Listbox } from "@usefragments/ui";Source

Preview

<Listbox aria-label="Options" defaultValue="first">
  <Listbox.Item value="first">First option</Listbox.Item>
  <Listbox.Item value="second">Second option</Listbox.Item>
  <Listbox.Item value="third">Third option</Listbox.Item>
</Listbox>

Usage

Use when

  • Results under a search field
  • Autocomplete suggestions
  • Command palette results
  • Any list the user drives with arrow keys

Avoid when

  • A list nobody selects from (use List)
  • Actions behind a button (use Menu)
  • A form field (use Select)
  • Navigation (use Sidebar or Tabs)

Implementation notes

  • Selection lives on the root: value / onValueChange, single or multiple
  • Give every Listbox.Item a value
  • Arrows, Home, End, Enter, and Space are handled for you
  • Always supply Listbox.Empty as a direct child for the no-match case
  • Group once the list draws from more than one source

Installation

pnpm add @usefragments/ui

Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Anatomy

Import Listbox once, then compose its parts with dot notation.

  • Listbox.Item
  • Listbox.Group
  • Listbox.Empty

Single selection owned by the root.

<Listbox aria-label="Options" defaultValue="first">
  <Listbox.Item value="first">First option</Listbox.Item>
  <Listbox.Item value="second">Second option</Listbox.Item>
  <Listbox.Item value="third">Third option</Listbox.Item>
</Listbox>

Examples

Controlled

The owner holds the value and hears every pick.

const [repo, setRepo] = React.useState("web");

<Listbox aria-label="Repository" value={repo} onValueChange={setRepo}>
  <Listbox.Item value="web">fragments-sdk/web</Listbox.Item>
  <Listbox.Item value="api">fragments-sdk/api</Listbox.Item>
</Listbox>

Multiple

Each pick toggles; the value is an array.

<Listbox aria-label="Labels" multiple defaultValue={['bug', 'ui']}>
  <Listbox.Item value="bug">Bug</Listbox.Item>
  <Listbox.Item value="ui">UI</Listbox.Item>
  <Listbox.Item value="docs">Docs</Listbox.Item>
</Listbox>

With Groups

Labelled sections once the list draws from more than one source.

<Listbox aria-label="Fruit">
  <Listbox.Group label="Orchard">
    <Listbox.Item value="apple">Apple</Listbox.Item>
    <Listbox.Item value="pear">Pear</Listbox.Item>
  </Listbox.Group>
  <Listbox.Group label="Stone fruit">

Empty State

A status line, not an option.

<Listbox aria-label="Search results">
  <Listbox.Empty>No results match "zebra".</Listbox.Empty>
</Listbox>

With Disabled Items

A disabled option is skipped by the keyboard and never picked.

<Listbox aria-label="Plan">
  <Listbox.Item value="pro">Pro</Listbox.Item>
  <Listbox.Item value="team" disabled>Team</Listbox.Item>
</Listbox>

API reference

Component props
Prop
Type
Default
Description
childrenRequiredReact.ReactNodeNot setListbox.Item, Listbox.Group, or Listbox.Empty components
defaultValuestring | string[] | null | undefinedNot setInitially selected value (uncontrolled)
disabledboolean | undefinedfalseDisables every option and removes the list from the tab order
Empty.childrenRequiredReact.ReactNodeNot set—
Group.childrenRequiredReact.ReactNodeNot set—
Group.labelstring | undefinedNot setGroup label
Item.childrenRequiredReact.ReactNodeNot set—
Item.disabledboolean | undefinedNot setWhether this option can be picked.
Item.valueRequiredstringNot setThe value this option selects.
multipleboolean | undefinedfalseEach pick toggles its option; value is an array
onValueChange((value: string) => void) | ((value: string[]) => void) | undefinedNot setCalled with the picked value, or the new array when multiple
valuestring | string[] | null | undefinedNot setSelected value (controlled); an array when multiple

Accessibility

  • Uses listbox and option roles with aria-activedescendant; one row is highlighted at a time
  • aria-selected marks picks, and a check repeats it without colour
  • aria-multiselectable is set in multiple mode
  • Listbox.Empty is a status line beside the listbox, never a fake option
  • Connect to an input with aria-controls for the full combobox pattern
  • InputsiblingPair with Input for search/autocomplete patterns
  • MenualternativeUse Menu when you need a trigger button
  • SelectalternativeUse Select for form field selection
  • ListalternativeUse List for static, non-interactive lists

Next steps