Siteplane UI

List Box

Choose one item from a visible list.

Markdown
PersonalPrivate drafts and experiments
TeamShared projects and billing
OrganizationCompany-wide members and settings
"use client";

import { ListBox, ListBoxItem } from "@/siteplane/components/list-box";

const workspaces = [
  {
    value: "personal",
    title: "Personal",
    description: "Private drafts and experiments",
  },
  {
    value: "team",
    title: "Team",
    description: "Shared projects and billing",
  },
  {
    value: "organization",
    title: "Organization",
    description: "Company-wide members and settings",
  },
];

export default function Example() {
  return (
    <ListBox aria-label="Workspace" className="max-w-sm" defaultValue="team">
      {workspaces.map((workspace) => (
        <ListBoxItem
          key={workspace.value}
          textValue={workspace.title}
          value={workspace.value}
        >
          <span className="grid min-w-0 gap-0.5 text-left">
            <span className="font-medium">{workspace.title}</span>
            <span className="text-muted-foreground text-xs">
              {workspace.description}
            </span>
          </span>
        </ListBoxItem>
      ))}
    </ListBox>
  );
}

Installation

pnpm dlx shadcn@4.18.0 add @siteplane/list-box

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/input

Field is installed with Input.

Usage

import { ListBox, ListBoxItem } from "@/siteplane/components/list-box";

<ListBox aria-label="Workspace">
  <ListBoxItem value="design">Design</ListBoxItem>
</ListBox>;

Composition

ListBox
└── ListBoxItem

Controlled selection

Control the selection with value and onValueChange; both use the item’s string value, not React Aria selectedKeys. Name the list with a visible FieldLabel and point aria-labelledby at it; disabled items stay visible but can’t be selected.

Design
Engineering
Archive
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { ListBox, ListBoxItem } from "@/siteplane/components/list-box";
import { useId, useState } from "react";

export default function Example() {
  const labelId = useId();
  const [value, setValue] = useState("design");
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel id={labelId}>Workspace</FieldLabel>
      <ListBox aria-labelledby={labelId} value={value} onValueChange={setValue}>
        <ListBoxItem value="design">Design</ListBoxItem>
        <ListBoxItem value="engineering">Engineering</ListBoxItem>
        <ListBoxItem value="archive" disabled>
          Archive
        </ListBoxItem>
      </ListBox>
    </Field>
  );
}

Row selection

Both appearances fill the selected row. The default, check, also shows a check that draws in when a row is selected and out when another row is picked, the way the Checkbox check does, and appears instantly under reduced motion. Set selectionAppearance="row" to drop the check, so the fill alone marks the selected row.

Board
List
Calendar
"use client";

import { ListBox, ListBoxItem } from "@/siteplane/components/list-box";

export default function Example() {
  return (
    <ListBox
      aria-label="Default view"
      className="max-w-sm"
      defaultValue="board"
      selectionAppearance="row"
    >
      <ListBoxItem value="board">Board</ListBoxItem>
      <ListBoxItem value="list">List</ListBoxItem>
      <ListBoxItem value="calendar">Calendar</ListBoxItem>
    </ListBox>
  );
}

API reference

Component properties
PropTypeDefaultDescription
ListBox.value / defaultValuestringuncontrolled / unsetSelected item value.
ListBox.onValueChange(value: string) => voidunsetReports the next non-empty selection.
ListBox.selectionAppearanceListBoxSelectionAppearance: check | rowcheckSelected-item treatment.
ListBox.sizesm | md | lg | xlprovider / lgShared item size.
ListBoxItem.valuestringrequiredStable item identity.
ListBoxItem.textValuestringderived from childrenPlain text for typeahead and rich options.
ListBoxItem.disabledbooleanfalsePrevents selection.
ListBoxItem.multilinebooleantrueAllows wrapped content.
ListBoxItem.data-slotstringlist-box-itemSlot override.

ListBoxProps extends native div props excluding role, aria-orientation and defaultValue. Its React Aria implementation fixes vertical orientation, single selection, replace behavior, disallowEmptySelection and focus wrapping. It does not expose multiple selection, selectedKeys or onSelectionChange. ListBoxItemProps extends ButtonBaseProps but omits align, aria-disabled, aria-selected, onClick, onKeyDown, role, render, size, tabIndex, value, variant and width; its own value is a required string. Item must be inside ListBox. The root owns selection and the item supplies its own ghost Button/React Aria composition. Both accept string className.

Read the installed source.

Usage notes

Arrow keys navigate the vertical list and typing uses text derived recursively from each item’s children. Give the list an accessible name. For rich items, supply ListBoxItem.textValue so typeahead matches only the option title. This component does not submit a native form value; serialize selected state yourself.