Skip to Content
Hashti Design System
Components

ListBox

Standalone accessible listbox component for single and multi-selection, option grouping, drag-and-drop reordering, and custom item templates.

ListBox

A ListBox displays a list of options and allows a user to select one or more items. While <Select> and <ComboBox> present a dropdown popup for picking options, a standalone ListBox renders the collection permanently inline on the page.

It is ideal for dual-list transfer pickers, permission managers, category selectors, command lists, and filter palettes. Hashti’s ListBox features compound subcomponents (ListBox.Item, ListBox.Section, ListBox.Header, ListBox.DragHandle, ListBox.Checkbox, ListBox.EmptyState), density sizing tiers, and keyboard roving navigation.

ListBoxAccessible list collection supporting keyboard roving navigation.
Darvazeh Portal
Hashti Vestibule
Dalan Corridor
Hayati Courtyard

Import

Hashti exports both compound and named subcomponents:

import { ListBox, ListBoxItem, ListBoxSection, ListBoxHeader } from "@hashti/react";
// Or via compound subcomponents:
// ListBox.Root, ListBox.Item, ListBox.Section, ListBox.Header, ListBox.DragHandle, ListBox.Checkbox, ListBox.EmptyState

Anatomy

<ListBox aria-label="Available permissions" selectionMode="multiple">
  <ListBox.Section>
    <ListBox.Header>Admin Roles</ListBox.Header>
    <ListBox.Item id="superadmin" textValue="Super Administrator">
      <ListBox.Checkbox />
      <span>Super Administrator</span>
    </ListBox.Item>
  </ListBox.Section>
</ListBox>

Examples

Standalone Single Selection

A classic inline listbox for picking a single option:

import { useState } from "react";
import { ListBox } from "@hashti/react";
import type { Selection } from "react-aria-components";

const themes = [
  { id: "core-white", name: "Hashti White", category: "Light" },
  { id: "core-g10", name: "Hashti Gray 10", category: "Light" },
  { id: "frame-light", name: "Frame Light", category: "Light" },
  { id: "relay-dark", name: "Relay Dark", category: "Dark" },
  { id: "frame-dark", name: "Frame Dark", category: "Dark" },
  { id: "core-g100", name: "Hashti Gray 100", category: "Dark" },
];

export function BasicListBoxExample() {
  const [selected, setSelected] = useState<Selection>(new Set(["frame-light"]));

  return (
    <div className="max-w-xs space-y-2">
      <h3 className="text-sm font-semibold">Active Theme</h3>
      <ListBox
        aria-label="Select theme"
        items={themes}
        selectionMode="single"
        selectedKeys={selected}
        onSelectionChange={setSelected}
        variant="bordered"
      >
        {(item) => (
          <ListBox.Item key={item.id} textValue={item.name}>
            <div className="flex justify-between items-center w-full py-1">
              <span className="text-sm font-medium">{item.name}</span>
              <span className="text-xs text-[var(--text-muted)]">{item.category}</span>
            </div>
          </ListBox.Item>
        )}
      </ListBox>
    </div>
  );
}

Multi-Selection with Checkboxes

Pair <ListBox.Checkbox /> with selectionMode="multiple" for picking several items:

import { useState } from "react";
import { ListBox } from "@hashti/react";
import type { Selection } from "react-aria-components";

const features = [
  { id: "a11y", name: "WCAG 2.2 AAA Contrast" },
  { id: "rtl", name: "Persian & Arabic RTL Dual-Engine" },
  { id: "tokens", name: "Semantic Token Integration" },
  { id: "rac", name: "React Aria Components 1.22 Primitives" },
];

export function MultiSelectListBoxExample() {
  const [selected, setSelected] = useState<Selection>(new Set(["a11y", "rtl"]));

  return (
    <div className="max-w-md space-y-3">
      <ListBox
        aria-label="Features"
        items={features}
        selectionMode="multiple"
        selectedKeys={selected}
        onSelectionChange={setSelected}
        variant="bordered"
      >
        {(feature) => (
          <ListBox.Item key={feature.id} textValue={feature.name}>
            <div className="flex items-center gap-3 py-1">
              <ListBox.Checkbox />
              <span className="text-sm">{feature.name}</span>
            </div>
          </ListBox.Item>
        )}
      </ListBox>
    </div>
  );
}

Sections and Group Headers

Organize options into categories using <ListBox.Section> and <ListBox.Header>:

import { ListBox } from "@hashti/react";

export function SectionedListBoxExample() {
  return (
    <div className="max-w-xs">
      <ListBox aria-label="Component categories" variant="bordered">
        <ListBox.Section>
          <ListBox.Header>Actions</ListBox.Header>
          <ListBox.Item id="btn">Button</ListBox.Item>
          <ListBox.Item id="lnk">Link</ListBox.Item>
          <ListBox.Item id="tb">Toolbar</ListBox.Item>
        </ListBox.Section>
        <ListBox.Section>
          <ListBox.Header>Forms</ListBox.Header>
          <ListBox.Item id="tf">Text Field</ListBox.Item>
          <ListBox.Item id="sel">Select</ListBox.Item>
          <ListBox.Item id="cb">ComboBox</ListBox.Item>
        </ListBox.Section>
      </ListBox>
    </div>
  );
}

Visual Variants & Density Sizes

Hashti supports three surface styling variants:

  • bordered (default): Crisp 1px border with row dividers.
  • flat: Transparent minimal frame with subtle item hover feedback.
  • card: Elevated card plinth (--surface-raised).

And two density sizes:

  • sm: Compact rows (32px target) for dense sidebars and modal drawers.
  • md: Standard comfortable rows (40px target).

Props Reference

ListBox (ListBox.Root)

PropTypeDefaultDescription
selectionMode'none' | 'single' | 'multiple''none'Allowed selection behavior.
selectedKeys'all' | Iterable<Key>—Controlled set of selected keys.
defaultSelectedKeys'all' | Iterable<Key>—Uncontrolled default selected keys.
onSelectionChange(keys: Selection) => void—Callback fired when selection changes.
disabledKeysIterable<Key>—Set of keys that cannot be selected.
variant'bordered' | 'flat' | 'card''bordered'Container background and border treatment.
size'sm' | 'md''md'Height density and item padding.
emptyContentReactNode—Content rendered when items array is empty.
aria-labelstring—Accessible description of the listbox.

ListBox.Item (ListBoxItem)

PropTypeDefaultDescription
idKey—Unique key for the item.
textValuestring—Plain text string used for typeahead letter navigation.
isDisabledbooleanfalseWhether the item is disabled.

Keyboard Interactions

Hashti ListBox adheres strictly to the WAI-ARIA Listbox Pattern:

KeyAction
↓ / ↑Navigates focus between items in the list.
Home / EndMoves focus directly to the first or last item.
SpaceToggles selection of the focused item in single and multiple modes.
Shift + ↓ / ↑Extends contiguous selection across multiple items.
⌘ + A / Ctrl + ASelects all selectable items in multiple selection mode.
Type-ahead (A–Z)Typing characters immediately jumps focus to the first matching option.

Hashti Token Bindings

TokenCSS VariableApplied State
Surface Background--surfaceListbox container background
Surface Hover--surface-hoverHover highlight on option rows
Selected Surface--selected (#e7edff)Active selected item tint
Focus Ring--focus2px focus ring on active option
Border Divider--divider1px perimeter border and section separator