ComboBoxUpdated
A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query
Import
import { ComboBox } from '@heroui/react';Usage
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Default() {Anatomy
Import the ComboBox component and access all parts using dot notation.
import { ComboBox, Input, Label, Description, Header, ListBox, Separator } from '@heroui/react';
export default () => (
<ComboBox>
<Label />
<ComboBox.InputGroup>
<Input />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<Description />
<ComboBox.Popover>
<ListBox>
<ListBox.Item>
<Label />
<Description />
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Section>
<Header />
<ListBox.Item>
<Label />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</ComboBox.Popover>
</ComboBox>
)With Description
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function WithDescription() {With Sections
"use client";
import {ComboBox, Header, Input, Label, ListBox, Separator} from "@heroui/react";
export function WithSections() {With Disabled Options
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function WithDisabledOptions() {Custom Indicator
"use client";
import {ChevronsExpandVertical} from "@gravity-ui/icons";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
Required
"use client";
import {Button, ComboBox, FieldError, Form, Input, Label, ListBox} from "@heroui/react";
export function Required() {Custom Value
"use client";
import {
Avatar,
AvatarFallback,Controlled
Selected: Cat
"use client";
import type {Key} from "@heroui/react";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";Controlled Input Value
Input value: (empty)
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
import {useState} from "react";
Asynchronous Loading
"use client";
import {
Collection,
ComboBox,Custom Filtering
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomFiltering() {Allows Custom Value
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function AllowsCustomValue() {Disabled
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Disabled() {Default Selected Key
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function DefaultSelectedKey() {Full Width
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function FullWidth() {
return (
<div className="w-[400px] space-y-4">On Surface
"use client";
import {Button, ComboBox, FieldError, Form, Input, Label, ListBox, Surface} from "@heroui/react";
export function OnSurface() {Styling
Passing Tailwind CSS classes
import { ComboBox, Input } from '@heroui/react';
function CustomComboBox() {
return (
<ComboBox className="w-full">
<Label>Favorite Animal</Label>
<ComboBox.InputGroup className="border rounded-lg p-2 bg-surface">
<Input placeholder="Search animals..." />
<ComboBox.Trigger className="text-muted" />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="1" textValue="Item 1" className="hover:bg-surface-secondary">
Item 1
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}Customizing the component classes
To customize the ComboBox component classes, you can use the @layer components directive.
Learn more.
@layer components {
.combobox {
@apply flex flex-col gap-1;
}
.combobox__input-group {
@apply relative inline-flex items-center;
}
.combobox__trigger {
@apply absolute right-0 text-muted;
}
.combobox__popover {
@apply rounded-lg border border-border bg-surface p-2;
}
}HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The ComboBox component uses these CSS classes (View source styles):
Base Classes
.combobox- Base combobox container.combobox__input-group- Container for the input and trigger button.combobox__trigger- The button that triggers the popover.combobox__popover- The popover container
State Classes
.combobox[data-invalid="true"]- Invalid state.combobox[data-disabled="true"]- Disabled combobox state.combobox__trigger[data-focus-visible="true"]- Focused trigger state.combobox__trigger[data-disabled="true"]- Disabled trigger state.combobox__trigger[data-open="true"]- Open trigger state
Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
- Hover:
:hoveror[data-hovered="true"]on trigger - Focus:
:focus-visibleor[data-focus-visible="true"]on trigger - Disabled:
:disabledor[data-disabled="true"]on combobox - Open:
[data-open="true"]on trigger
API Reference
ComboBox Props
| Prop | Type | Default | Description |
|---|---|---|---|
inputValue | string | - | Current input value (controlled) |
defaultInputValue | string | - | Default input value (uncontrolled) |
onInputChange | (value: string) => void | - | Handler called when the input value changes |
selectedKey | Key | null | - | Current selected key (controlled) |
defaultSelectedKey | Key | null | - | Default selected key (uncontrolled) |
onSelectionChange | (key: Key | null) => void | - | Handler called when the selection changes |
isOpen | boolean | - | Sets the open state of the popover (controlled) |
defaultOpen | boolean | - | Sets the default open state of the popover (uncontrolled) |
onOpenChange | (isOpen: boolean) => void | - | Handler called when the open state changes |
disabledKeys | Iterable<Key> | - | Keys of disabled items |
isDisabled | boolean | - | Whether the combobox is disabled |
isRequired | boolean | - | Whether user input is required |
isInvalid | boolean | - | Whether the combobox value is invalid |
name | string | - | The name of the input, used when submitting an HTML form |
autoComplete | string | - | Describes the type of autocomplete functionality |
allowsCustomValue | boolean | - | Whether the combobox allows custom values not in the list |
allowsEmptyCollection | boolean | - | Whether the combobox allows an empty collection |
defaultFilter | (text: string, inputValue: string) => boolean | - | Custom filter function for filtering items |
items | Iterable<T> | - | The items to display in the listbox |
fullWidth | boolean | false | Whether the combobox should take full width of its container |
className | string | - | Additional CSS classes |
children | ReactNode | RenderFunction | - | ComboBox content or render function |
ComboBox.InputGroup Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | InputGroup content |
ComboBox.Trigger Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Custom trigger content |
ComboBox.Popover Props
| Prop | Type | Default | Description |
|---|---|---|---|
placement | "bottom" | "bottom left" | "bottom right" | "bottom start" | "bottom end" | "top" | "top left" | "top right" | "top start" | "top end" | "left" | "left top" | "left bottom" | "start" | "start top" | "start bottom" | "right" | "right top" | "right bottom" | "end" | "end top" | "end bottom" | "bottom" | Placement of the popover relative to the trigger |
className | string | - | Additional CSS classes |
children | ReactNode | - | Content children |
RenderProps
When using render functions with ComboBox, these values are provided:
| Prop | Type | Description |
|---|---|---|
state | ComboBoxState | The state of the combobox |
inputValue | string | The current input value |
selectedKey | Key | null | The currently selected key |
selectedItem | Node | null | The currently selected item |
Examples
Basic Usage
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
<ComboBox className="w-[256px]">
<Label>Favorite Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>With Sections
import { ComboBox, Input, Label, ListBox, Header, Separator } from '@heroui/react';
<ComboBox className="w-[256px]">
<Label>Country</Label>
<ComboBox.InputGroup>
<Input placeholder="Search countries..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Section>
<Header>North America</Header>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
<Separator />
<ListBox.Section>
<Header>Europe</Header>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</ComboBox.Popover>
</ComboBox>Controlled Selection
import type { Key } from '@heroui/react';
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
import { useState } from 'react';
function ControlledComboBox() {
const [selectedKey, setSelectedKey] = useState<Key | null>('cat');
return (
<ComboBox
className="w-[256px]"
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
>
<Label>Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}Controlled Input Value
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
import { useState } from 'react';
function ControlledInputComboBox() {
const [inputValue, setInputValue] = useState('');
return (
<ComboBox
className="w-[256px]"
inputValue={inputValue}
onInputChange={setInputValue}
>
<Label>Search</Label>
<ComboBox.InputGroup>
<Input placeholder="Type to search..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}Asynchronous Loading
import { Collection, ComboBox, EmptyState, Input, Label, ListBox, ListBoxLoadMoreItem, Spinner } from '@heroui/react';
import { useAsyncList } from '@react-stately/data';
interface Character {
name: string;
}
function AsyncComboBox() {
const list = useAsyncList<Character>({
async load({cursor, filterText, signal}) {
const res = await fetch(
cursor || `https://swapi.py4e.com/api/people/?search=${filterText}`,
{ signal }
);
const json = await res.json();
return {
items: json.results,
cursor: json.next,
};
},
});
return (
<ComboBox
allowsEmptyCollection
className="w-[256px]"
inputValue={list.filterText}
onInputChange={list.setFilterText}
>
<Label>Pick a Character</Label>
<ComboBox.InputGroup>
<Input placeholder="Star Wars characters..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox renderEmptyState={() => <EmptyState />}>
<Collection items={list.items}>
{(item) => (
<ListBox.Item id={item.name} textValue={item.name}>
{item.name}
<ListBox.ItemIndicator />
</ListBox.Item>
)}
</Collection>
<ListBoxLoadMoreItem
isLoading={list.loadingState === "loadingMore"}
onLoadMore={list.loadMore}
>
<div className="flex items-center justify-center gap-2 py-2">
<Spinner size="sm" />
<span className="text-sm text-muted">Loading more...</span>
</div>
</ListBoxLoadMoreItem>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}Custom Filtering
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
<ComboBox
className="w-[256px]"
defaultFilter={(text, inputValue) => {
if (!inputValue) return true;
return text.toLowerCase().includes(inputValue.toLowerCase());
}}
>
<Label>Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>Accessibility
The ComboBox component implements the ARIA combobox pattern and provides:
- Full keyboard navigation support
- Screen reader announcements for selection changes and input changes
- Proper focus management
- Support for disabled states
- Typeahead search functionality
- HTML form integration
- Support for custom values
For more information, see the React Aria ComboBox documentation.





