ListBoxPreview

A listbox displays a list of options and allows a user to select one or more of them

Import

import { ListBox } from '@heroui/react';

Usage

B
bob@heroui.com
F
fred@heroui.com
M
martha@heroui.com
import {Avatar, AvatarFallback, AvatarImage, Description, Label, ListBox} from "@heroui/react";

export function Default() {
  return (
    <ListBox aria-label="Users" className="w-[220px]" selectionMode="single">
      <ListBox.Item id="1" textValue="Bob">
        <Avatar size="sm">
          <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" />
          <AvatarFallback>B</AvatarFallback>
        </Avatar>
        <div className="flex flex-col">
          <Label>Bob</Label>
          <Description>bob@heroui.com</Description>
        </div>
        <ListBox.ItemIndicator />
      </ListBox.Item>
      <ListBox.Item id="2" textValue="Fred">
        <Avatar size="sm">
          <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" />
          <AvatarFallback>F</AvatarFallback>
        </Avatar>
        <div className="flex flex-col">
          <Label>Fred</Label>
          <Description>fred@heroui.com</Description>
        </div>
        <ListBox.ItemIndicator />
      </ListBox.Item>
      <ListBox.Item id="3" textValue="Martha">
        <Avatar size="sm">
          <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" />
          <AvatarFallback>M</AvatarFallback>
        </Avatar>
        <div className="flex flex-col">
          <Label>Martha</Label>
          <Description>martha@heroui.com</Description>
        </div>
        <ListBox.ItemIndicator />
      </ListBox.Item>
    </ListBox>
  );
}

Anatomy

Import the ListBox component and access all parts using dot notation.

import { ListBox, Label, Description, Header } from '@heroui/react';

export default () => (
  <ListBox>
    <ListBox.Item>
      <Label />
      <Description />
      <ListBox.ItemIndicator />
    </ListBox.Item>
    <ListBox.Section>
      <Header />
      <ListBox.Item>
        <Label />
      </ListBox.Item>
    </ListBox.Section>
  </ListBox>
)

With Sections

Create a new file
N
Make changes
E
Move to trash
D
"use client";

import {Description, Header, Kbd, Label, ListBox, Separator, Surface} from "@heroui/react";
import {Icon} from "@iconify/react";

export function WithSections() {
  return (
    <Surface className="shadow-surface w-[256px] rounded-3xl">
      <ListBox
        aria-label="File actions"
        className="w-full p-2"
        selectionMode="none"
        onAction={(key) => alert(`Selected item: ${key}`)}
      >
        <ListBox.Section>
          <Header>Actions</Header>
          <ListBox.Item id="new-file" textValue="New file">
            <div className="flex h-8 items-start justify-center pt-px">
              <Icon className="text-muted size-4 flex-shrink-0" icon="gravity-ui:square-plus" />
            </div>
            <div className="flex flex-col">
              <Label>New file</Label>
              <Description>Create a new file</Description>
            </div>
            <Kbd className="ms-auto" variant="light">
              <Kbd.Abbr keyValue="command" />
              <Kbd.Content>N</Kbd.Content>
            </Kbd>
          </ListBox.Item>
          <ListBox.Item id="edit-file" textValue="Edit file">
            <div className="flex h-8 items-start justify-center pt-px">
              <Icon className="text-muted size-4 flex-shrink-0" icon="gravity-ui:pencil" />
            </div>
            <div className="flex flex-col">
              <Label>Edit file</Label>
              <Description>Make changes</Description>
            </div>
            <Kbd className="ms-auto" variant="light">
              <Kbd.Abbr keyValue="command" />
              <Kbd.Content>E</Kbd.Content>
            </Kbd>
          </ListBox.Item>
        </ListBox.Section>
        <Separator />
        <ListBox.Section>
          <Header>Danger zone</Header>
          <ListBox.Item id="delete-file" textValue="Delete file" variant="danger">
            <div className="flex h-8 items-start justify-center pt-px">
              <Icon className="text-danger size-4 flex-shrink-0" icon="gravity-ui:trash-bin" />
            </div>
            <div className="flex flex-col">
              <Label>Delete file</Label>
              <Description>Move to trash</Description>
            </div>
            <Kbd className="ms-auto" variant="light">
              <Kbd.Abbr keyValue="command" />
              <Kbd.Abbr keyValue="shift" />
              <Kbd.Content>D</Kbd.Content>
            </Kbd>
          </ListBox.Item>
        </ListBox.Section>
      </ListBox>
    </Surface>
  );
}

Multi Select

B
bob@heroui.com
F
fred@heroui.com
M
martha@heroui.com
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
  Description,
  Label,
  ListBox,
  Surface,
} from "@heroui/react";

export function MultiSelect() {
  return (
    <Surface className="shadow-surface w-[256px] rounded-3xl">
      <ListBox aria-label="Users" selectionMode="multiple">
        <ListBox.Item id="1" textValue="Bob">
          <Avatar size="sm">
            <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" />
            <AvatarFallback>B</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <Label>Bob</Label>
            <Description>bob@heroui.com</Description>
          </div>
          <ListBox.ItemIndicator />
        </ListBox.Item>
        <ListBox.Item id="2" textValue="Fred">
          <Avatar size="sm">
            <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" />
            <AvatarFallback>F</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <Label>Fred</Label>
            <Description>fred@heroui.com</Description>
          </div>
          <ListBox.ItemIndicator />
        </ListBox.Item>
        <ListBox.Item id="3" textValue="Martha">
          <Avatar size="sm">
            <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" />
            <AvatarFallback>M</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <Label>Martha</Label>
            <Description>martha@heroui.com</Description>
          </div>
          <ListBox.ItemIndicator />
        </ListBox.Item>
      </ListBox>
    </Surface>
  );
}

With Disabled Items

Create a new file
N
Make changes
E
Move to trash
D
"use client";

import {Description, Header, Kbd, Label, ListBox, Separator, Surface} from "@heroui/react";
import {Icon} from "@iconify/react";

export function WithDisabledItems() {
  return (
    <Surface className="shadow-surface w-[256px] rounded-3xl">
      <ListBox
        aria-label="File actions"
        className="w-full p-2"
        disabledKeys={["delete-file"]}
        selectionMode="none"
        onAction={(key) => alert(`Selected item: ${key}`)}
      >
        <ListBox.Section>
          <Header>Actions</Header>
          <ListBox.Item id="new-file" textValue="New file">
            <div className="flex h-8 items-start justify-center pt-px">
              <Icon className="text-muted size-4 flex-shrink-0" icon="gravity-ui:square-plus" />
            </div>
            <div className="flex flex-col">
              <Label>New file</Label>
              <Description>Create a new file</Description>
            </div>
            <Kbd className="ms-auto" variant="light">
              <Kbd.Abbr keyValue="command" />
              <Kbd.Content>N</Kbd.Content>
            </Kbd>
          </ListBox.Item>
          <ListBox.Item id="edit-file" textValue="Edit file">
            <div className="flex h-8 items-start justify-center pt-px">
              <Icon className="text-muted size-4 flex-shrink-0" icon="gravity-ui:pencil" />
            </div>
            <div className="flex flex-col">
              <Label>Edit file</Label>
              <Description>Make changes</Description>
            </div>
            <Kbd className="ms-auto" variant="light">
              <Kbd.Abbr keyValue="command" />
              <Kbd.Content>E</Kbd.Content>
            </Kbd>
          </ListBox.Item>
        </ListBox.Section>
        <Separator />
        <ListBox.Section>
          <Header>Danger zone</Header>
          <ListBox.Item id="delete-file" textValue="Delete file" variant="danger">
            <div className="flex h-8 items-start justify-center pt-px">
              <Icon className="text-danger size-4 flex-shrink-0" icon="gravity-ui:trash-bin" />
            </div>
            <div className="flex flex-col">
              <Label>Delete file</Label>
              <Description>Move to trash</Description>
            </div>
            <Kbd className="ms-auto" variant="light">
              <Kbd.Abbr keyValue="command" />
              <Kbd.Abbr keyValue="shift" />
              <Kbd.Content>D</Kbd.Content>
            </Kbd>
          </ListBox.Item>
        </ListBox.Section>
      </ListBox>
    </Surface>
  );
}

Custom Check Icon

B
bob@heroui.com
F
fred@heroui.com
M
martha@heroui.com
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
  Description,
  Label,
  ListBox,
  Surface,
} from "@heroui/react";
import {Icon} from "@iconify/react";

export function CustomCheckIcon() {
  return (
    <Surface className="shadow-surface w-[256px] rounded-3xl">
      <ListBox aria-label="Users" selectionMode="multiple">
        <ListBox.Item id="1" textValue="Bob">
          <Avatar size="sm">
            <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" />
            <AvatarFallback>B</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <Label>Bob</Label>
            <Description>bob@heroui.com</Description>
          </div>
          <ListBox.ItemIndicator>
            {({isSelected}) =>
              isSelected ? <Icon className="text-accent size-4" icon="gravity-ui:check" /> : null
            }
          </ListBox.ItemIndicator>
        </ListBox.Item>
        <ListBox.Item id="2" textValue="Fred">
          <Avatar size="sm">
            <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" />
            <AvatarFallback>F</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <Label>Fred</Label>
            <Description>fred@heroui.com</Description>
          </div>
          <ListBox.ItemIndicator>
            {({isSelected}) =>
              isSelected ? <Icon className="text-accent size-4" icon="gravity-ui:check" /> : null
            }
          </ListBox.ItemIndicator>
        </ListBox.Item>
        <ListBox.Item id="3" textValue="Martha">
          <Avatar size="sm">
            <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" />
            <AvatarFallback>M</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <Label>Martha</Label>
            <Description>martha@heroui.com</Description>
          </div>
          <ListBox.ItemIndicator>
            {({isSelected}) =>
              isSelected ? <Icon className="text-accent size-4" icon="gravity-ui:check" /> : null
            }
          </ListBox.ItemIndicator>
        </ListBox.Item>
      </ListBox>
    </Surface>
  );
}

Controlled

B
bob@heroui.com
F
fred@heroui.com
M
martha@heroui.com

Selected: 1

"use client";

import type {Selection} from "@heroui/react";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
  Description,
  Label,
  ListBox,
  Surface,
} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";

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

  const selectedItems = Array.from(selected);

  return (
    <div className="space-y-4">
      <Surface className="shadow-surface w-[256px] rounded-3xl">
        <ListBox
          aria-label="Users"
          selectedKeys={selected}
          selectionMode="multiple"
          onSelectionChange={setSelected}
        >
          <ListBox.Item id="1" textValue="Bob">
            <Avatar size="sm">
              <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" />
              <AvatarFallback>B</AvatarFallback>
            </Avatar>
            <div className="flex flex-col">
              <Label>Bob</Label>
              <Description>bob@heroui.com</Description>
            </div>
            <ListBox.ItemIndicator>
              {({isSelected}) =>
                isSelected ? <Icon className="text-accent size-4" icon="gravity-ui:check" /> : null
              }
            </ListBox.ItemIndicator>
          </ListBox.Item>
          <ListBox.Item id="2" textValue="Fred">
            <Avatar size="sm">
              <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" />
              <AvatarFallback>F</AvatarFallback>
            </Avatar>
            <div className="flex flex-col">
              <Label>Fred</Label>
              <Description>fred@heroui.com</Description>
            </div>
            <ListBox.ItemIndicator>
              {({isSelected}) =>
                isSelected ? <Icon className="text-accent size-4" icon="gravity-ui:check" /> : null
              }
            </ListBox.ItemIndicator>
          </ListBox.Item>
          <ListBox.Item id="3" textValue="Martha">
            <Avatar size="sm">
              <AvatarImage src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" />
              <AvatarFallback>M</AvatarFallback>
            </Avatar>
            <div className="flex flex-col">
              <Label>Martha</Label>
              <Description>martha@heroui.com</Description>
            </div>
            <ListBox.ItemIndicator>
              {({isSelected}) =>
                isSelected ? <Icon className="text-accent size-4" icon="gravity-ui:check" /> : null
              }
            </ListBox.ItemIndicator>
          </ListBox.Item>
        </ListBox>
      </Surface>
      <p className="text-sm text-neutral-500">
        Selected: {selectedItems.length > 0 ? selectedItems.join(", ") : "None"}
      </p>
    </div>
  );
}

Styling

Passing Tailwind CSS classes

import { ListBox } from '@heroui/react';

function CustomListBox() {
  return (
    <ListBox className="border rounded-lg p-2 bg-surface">
      <ListBox.Item id="1" textValue="Item 1" className="hover:bg-surface-secondary">
        Item 1
      </ListBox.Item>
    </ListBox>
  );
}

Customizing the component classes

To customize the ListBox component classes, you can use the @layer components directive.
Learn more.

@layer components {
  .listbox {
    @apply rounded-lg border border-border bg-surface p-2;
  }

  .listbox-item {
    @apply rounded px-2 py-1 cursor-pointer;
  }

  .listbox-item--danger {
    @apply text-danger;
  }

  .listbox-item__indicator {
    @apply text-accent;
  }
}

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The ListBox component uses these CSS classes (View source styles):

Base Classes

  • .listbox - Base listbox container
  • .listbox-item - Individual listbox item
  • .listbox-item__indicator - Selection indicator icon
  • .listbox-section - Section container for grouping items

Variant Classes

  • .listbox--default - Default variant styling
  • .listbox--danger - Danger variant styling
  • .listbox-item--default - Default item variant
  • .listbox-item--danger - Danger item variant

State Classes

  • .listbox-item[data-selected="true"] - Selected item state
  • .listbox-item[data-focus-visible="true"] - Focused item state
  • .listbox-item[data-disabled="true"] - Disabled item state
  • .listbox-item__indicator[data-visible="true"] - Visible indicator state

Interactive States

The component supports both CSS pseudo-classes and data attributes for flexibility:

  • Hover: :hover or [data-hovered="true"] on item
  • Focus: :focus-visible or [data-focus-visible="true"] on item
  • Selected: [data-selected="true"] on item
  • Disabled: :disabled or [data-disabled="true"] on item

API Reference

ListBox Props

PropTypeDefaultDescription
aria-labelstring-Accessibility label for the listbox
aria-labelledbystring-ID of element that labels the listbox
selectionMode"none" | "single" | "multiple""single"Selection behavior
selectedKeysSelection-Controlled selected keys
defaultSelectedKeysSelection-Initial selected keys
onSelectionChange(keys: Selection) => void-Handler called when selection changes
disabledKeysIterable<Key>-Keys of disabled items
onAction(key: Key) => void-Handler called when an item is activated
variant"default" | "danger""default"Visual variant
classNamestring-Additional CSS classes
childrenReactNode-ListBox items and sections

ListBox.Item Props

PropTypeDefaultDescription
idKey-Unique identifier for the item
textValuestring-Text value for accessibility and typeahead
isDisabledbooleanfalseWhether this item is disabled
variant"default" | "danger""default"Visual variant
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-Item content or render function

ListBox.ItemIndicator Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-Custom indicator content or render function

ListBox.Section Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReactNode-Section content including Header and Items

RenderProps

When using render functions with ListBox.Item or ListBox.ItemIndicator, these values are provided:

PropTypeDescription
isSelectedbooleanWhether the item is selected
isFocusedbooleanWhether the item is focused
isDisabledbooleanWhether the item is disabled
isPressedbooleanWhether the item is being pressed

Examples

Basic Usage

import { ListBox, Label, Description } from '@heroui/react';

<ListBox aria-label="Users" selectionMode="single">
  <ListBox.Item id="1" textValue="Bob">
    <Label>Bob</Label>
    <Description>bob@heroui.com</Description>
  </ListBox.Item>
  <ListBox.Item id="2" textValue="Alice">
    <Label>Alice</Label>
    <Description>alice@heroui.com</Description>
  </ListBox.Item>
</ListBox>

With Sections

import { ListBox, Header, Separator } from '@heroui/react';

<ListBox aria-label="Actions" selectionMode="none" onAction={(key) => console.log(key)}>
  <ListBox.Section>
    <Header>Actions</Header>
    <ListBox.Item id="new" textValue="New file">New file</ListBox.Item>
    <ListBox.Item id="edit" textValue="Edit file">Edit file</ListBox.Item>
  </ListBox.Section>
  <Separator />
  <ListBox.Section>
    <Header>Danger zone</Header>
    <ListBox.Item id="delete" textValue="Delete" variant="danger">Delete</ListBox.Item>
  </ListBox.Section>
</ListBox>

Controlled Selection

import { ListBox, Selection } from '@heroui/react';
import { useState } from 'react';

function ControlledListBox() {
  const [selected, setSelected] = useState<Selection>(new Set(["1"]));

  return (
    <ListBox
      aria-label="Options"
      selectedKeys={selected}
      selectionMode="multiple"
      onSelectionChange={setSelected}
    >
      <ListBox.Item id="1" textValue="Option 1">Option 1</ListBox.Item>
      <ListBox.Item id="2" textValue="Option 2">Option 2</ListBox.Item>
      <ListBox.Item id="3" textValue="Option 3">Option 3</ListBox.Item>
    </ListBox>
  );
}

Custom Indicator

import { ListBox, ListBoxItemIndicator } from '@heroui/react';
import { Icon } from '@iconify/react';

<ListBox aria-label="Options" selectionMode="multiple">
  <ListBox.Item id="1" textValue="Option 1">
    Option 1
    <ListBox.ItemIndicator>
      {({isSelected}) =>
        isSelected ? <Icon icon="gravity-ui:check" /> : null
      }
    </ListBox.ItemIndicator>
  </ListBox.Item>
</ListBox>

Accessibility

The ListBox component implements the ARIA listbox pattern and provides:

  • Full keyboard navigation support
  • Screen reader announcements for selection changes
  • Proper focus management
  • Support for disabled states
  • Typeahead search functionality

For more information, see the React Aria ListBox documentation.