HeroUI

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

Search and select your favorite animal
"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

You can type any animal name, even if it's not in the list
"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: :hover or [data-hovered="true"] on trigger
  • Focus: :focus-visible or [data-focus-visible="true"] on trigger
  • Disabled: :disabled or [data-disabled="true"] on combobox
  • Open: [data-open="true"] on trigger

API Reference

ComboBox Props

PropTypeDefaultDescription
inputValuestring-Current input value (controlled)
defaultInputValuestring-Default input value (uncontrolled)
onInputChange(value: string) => void-Handler called when the input value changes
selectedKeyKey | null-Current selected key (controlled)
defaultSelectedKeyKey | null-Default selected key (uncontrolled)
onSelectionChange(key: Key | null) => void-Handler called when the selection changes
isOpenboolean-Sets the open state of the popover (controlled)
defaultOpenboolean-Sets the default open state of the popover (uncontrolled)
onOpenChange(isOpen: boolean) => void-Handler called when the open state changes
disabledKeysIterable<Key>-Keys of disabled items
isDisabledboolean-Whether the combobox is disabled
isRequiredboolean-Whether user input is required
isInvalidboolean-Whether the combobox value is invalid
namestring-The name of the input, used when submitting an HTML form
autoCompletestring-Describes the type of autocomplete functionality
allowsCustomValueboolean-Whether the combobox allows custom values not in the list
allowsEmptyCollectionboolean-Whether the combobox allows an empty collection
defaultFilter(text: string, inputValue: string) => boolean-Custom filter function for filtering items
itemsIterable<T>-The items to display in the listbox
fullWidthbooleanfalseWhether the combobox should take full width of its container
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-ComboBox content or render function

ComboBox.InputGroup Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReactNode-InputGroup content

ComboBox.Trigger Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReactNode-Custom trigger content

ComboBox.Popover Props

PropTypeDefaultDescription
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
classNamestring-Additional CSS classes
childrenReactNode-Content children

RenderProps

When using render functions with ComboBox, these values are provided:

PropTypeDescription
stateComboBoxStateThe state of the combobox
inputValuestringThe current input value
selectedKeyKey | nullThe currently selected key
selectedItemNode | nullThe 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.

On this page