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
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
"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
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
"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
"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
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:
:hoveror[data-hovered="true"]on item - Focus:
:focus-visibleor[data-focus-visible="true"]on item - Selected:
[data-selected="true"]on item - Disabled:
:disabledor[data-disabled="true"]on item
API Reference
ListBox Props
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | - | Accessibility label for the listbox |
aria-labelledby | string | - | ID of element that labels the listbox |
selectionMode | "none" | "single" | "multiple" | "single" | Selection behavior |
selectedKeys | Selection | - | Controlled selected keys |
defaultSelectedKeys | Selection | - | Initial selected keys |
onSelectionChange | (keys: Selection) => void | - | Handler called when selection changes |
disabledKeys | Iterable<Key> | - | Keys of disabled items |
onAction | (key: Key) => void | - | Handler called when an item is activated |
variant | "default" | "danger" | "default" | Visual variant |
className | string | - | Additional CSS classes |
children | ReactNode | - | ListBox items and sections |
ListBox.Item Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | Key | - | Unique identifier for the item |
textValue | string | - | Text value for accessibility and typeahead |
isDisabled | boolean | false | Whether this item is disabled |
variant | "default" | "danger" | "default" | Visual variant |
className | string | - | Additional CSS classes |
children | ReactNode | RenderFunction | - | Item content or render function |
ListBox.ItemIndicator Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | RenderFunction | - | Custom indicator content or render function |
ListBox.Section Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Section content including Header and Items |
RenderProps
When using render functions with ListBox.Item or ListBox.ItemIndicator, these values are provided:
| Prop | Type | Description |
|---|---|---|
isSelected | boolean | Whether the item is selected |
isFocused | boolean | Whether the item is focused |
isDisabled | boolean | Whether the item is disabled |
isPressed | boolean | Whether 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.