ComboBox
Autocomplete input combining a text field with a filtered popup listbox for item lookups.
ComboBox
The ComboBox component merges a single-line text input with a filtered dropdown listbox. Users can either type directly to filter through available options or click the dropdown trigger to browse the full set.
ComboBox is particularly suited for high-density forms, long dataset lookups (such as geographic regions, currencies, or user directories), and cases where custom values can optionally be typed.
Import
import { ComboBox, ListBoxItem } from "@hashti/react";
Sub-parts for advanced composition:
import {
ComboBox,
FieldControl,
FieldGroupInput,
FieldButton,
Popover,
ListBox,
} from "@hashti/react";
Examples
Basic Autocomplete Filtering
A standard ComboBox that automatically filters suggestions matching the user’s typed input:
import { ComboBox, ListBoxItem } from "@hashti/react";
export function BasicComboBoxDemo() {
return (
<div className="max-w-xs">
<ComboBox label="Persian UNESCO World Heritage Site" placeholder="Type to search...">
<ListBoxItem id="persepolis">Persepolis (تخت جمشید)</ListBoxItem>
<ListBoxItem id="naqsh-e-jahan">Naqsh-e Jahan Square (میدان نقش جهان)</ListBoxItem>
<ListBoxItem id="pasargadae">Pasargadae (پاسارگاد)</ListBoxItem>
<ListBoxItem id="yazd">Historic City of Yazd (شهر تاریخی یزد)</ListBoxItem>
<ListBoxItem id="golestan">Golestan Palace (کاخ گلستان)</ListBoxItem>
<ListBoxItem id="chogha-zanbil">Chogha Zanbil (چغازنبیل)</ListBoxItem>
</ComboBox>
</div>
);
}
Controlled Input & Selection
Track both the typed text (inputValue) and the selected key (selectedKey):
import { useState } from "react";
import { ComboBox, ListBoxItem } from "@hashti/react";
import type { Key } from "react-aria-components";
export function ControlledComboBoxDemo() {
const [selectedKey, setSelectedKey] = useState<Key | null>(null);
const [inputValue, setInputValue] = useState("");
return (
<div className="max-w-xs flex flex-col gap-3">
<ComboBox
label="Select Artisan Workshop"
placeholder="Search workshops..."
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
inputValue={inputValue}
onInputChange={setInputValue}
description="Choose or type a workshop location."
>
<ListBoxItem id="isfahan-tile">Isfahan Glazed Tile Atelier</ListBoxItem>
<ListBoxItem id="kashan-rose">Kashan Rosewater Distillery</ListBoxItem>
<ListBoxItem id="shiraz-carpet">Shiraz Nomadic Carpet Loom</ListBoxItem>
<ListBoxItem id="yazd-silk">Yazd Termeh Silk Weaving</ListBoxItem>
</ComboBox>
<div className="text-xs text-gray-500">
Selected Key: <b>{String(selectedKey ?? "none")}</b> | Input: <b>"{inputValue}"</b>
</div>
</div>
);
}
Allowing Custom Values
Set allowsCustomValue to enable users to type and submit free-form text that isn’t present in the list of suggestions:
import { ComboBox, ListBoxItem } from "@hashti/react";
export function CustomValueComboBoxDemo() {
return (
<div className="max-w-xs">
<ComboBox
label="Project Tag / Category"
placeholder="Pick existing or type new..."
allowsCustomValue
description="Select an existing architectural category or type your own."
>
<ListBoxItem id="restoration">Restoration</ListBoxItem>
<ListBoxItem id="vernacular">Vernacular</ListBoxItem>
<ListBoxItem id="sustainability">Sustainability</ListBoxItem>
<ListBoxItem id="structural">Structural Masonry</ListBoxItem>
</ComboBox>
</div>
);
}
Custom Item Rendering with Descriptions
ListBox items can render custom layouts including secondary titles, status badges, and metadata:
import { ComboBox, ListBoxItem } from "@hashti/react";
interface Member {
id: string;
name: string;
role: string;
department: string;
}
const MEMBERS: Member[] = [
{ id: "1", name: "Darius Jamshidi", role: "Principal Architect", department: "Restoration" },
{ id: "2", name: "Anahita Moradi", role: "Structural Engineer", department: "Vaulting" },
{ id: "3", name: "Cyrus Tavakoli", role: "Materials Specialist", department: "Ceramics" },
];
export function RichItemComboBoxDemo() {
return (
<div className="max-w-sm">
<ComboBox label="Assign Team Lead" items={MEMBERS} placeholder="Filter by name or role...">
{(member) => (
<ListBoxItem id={member.id} textValue={member.name}>
<div className="flex flex-col py-1">
<span className="font-semibold text-sm">{member.name}</span>
<span className="text-xs text-gray-500">
{member.role} • {member.department}
</span>
</div>
</ListBoxItem>
)}
</ComboBox>
</div>
);
}
Multi-Selection with Chips or Badges
When selectionMode="multiple" is enabled, users can select several options. The multipleDisplay prop governs how selected choices appear:
'chips'(default): Interactive tags with removable × buttons rendered directly below the input field.'badge': A compact counter badge showing the number of selected items.'text': Plain text description.
import { useState } from "react";
import { ComboBox, ListBoxItem } from "@hashti/react";
import type { Selection } from "react-aria-components";
export function MultiComboBoxDemo() {
const [selected, setSelected] = useState<Selection>(new Set(["fuma", "react"]));
return (
<div className="max-w-md space-y-4">
<ComboBox
label="Select Frameworks & Tools"
placeholder="Type or pick multiple tools..."
selectionMode="multiple"
multipleDisplay="chips"
selectedKeys={selected}
onSelectionChange={setSelected}
>
<ListBoxItem id="react">React 19</ListBoxItem>
<ListBoxItem id="astro">Astro v5</ListBoxItem>
<ListBoxItem id="fuma">Fumadocs</ListBoxItem>
<ListBoxItem id="tailwind">Tailwind CSS v4</ListBoxItem>
<ListBoxItem id="bun">Bun Runtime</ListBoxItem>
</ComboBox>
</div>
);
}
Validation Error State
import { ComboBox, ListBoxItem } from "@hashti/react";
export function InvalidComboBoxDemo() {
return (
<div className="max-w-xs">
<ComboBox
label="Geographic Zone"
placeholder="Select zone..."
isInvalid
errorMessage="Please select an approved conservation zone."
>
<ListBoxItem id="zone-a">Zone A: Central Historic Core</ListBoxItem>
<ListBoxItem id="zone-b">Zone B: Buffer Perimeter</ListBoxItem>
</ComboBox>
</div>
);
}
Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
selectionMode | 'single' | 'multiple' | 'single' | Allowed selection mode (single item or multi-select). |
multipleDisplay | 'chips' | 'badge' | 'text' | 'chips' | Display format for multi-selected items in the combobox. |
items | Iterable<T> | — | Dynamic list of items for autocomplete. |
selectedKey | Key | null | — | Currently selected item key (controlled). |
defaultSelectedKey | Key | — | Initially selected item key (uncontrolled). |
onSelectionChange | (key: Key | null) => void | — | Callback fired when an item is selected. |
inputValue | string | — | Text content within the input field (controlled). |
defaultInputValue | string | — | Default text content for the input field. |
onInputChange | (value: string) => void | — | Callback fired as the user types into the input. |
allowsCustomValue | boolean | false | When true, accepts free-form text outside the suggested options. |
placeholder | string | — | Placeholder text when the field is empty. |
label | ReactNode | — | Accessible label for the input. |
description | ReactNode | — | Explanatory text beneath or above the control. |
errorMessage | ReactNode | — | Error text shown when isInvalid is true. |
size | 'sm' | 'md' | 'lg' | 'md' | Height and control sizing. |
isDisabled | boolean | false | Whether the entire combobox is disabled. |
isInvalid | boolean | false | Whether validation has failed. |
isRequired | boolean | false | Marks the combobox as mandatory for submission. |
disabledKeys | Iterable<Key> | — | Keys of specific options that should be disabled. |
Accessibility
ComboBox complies with the WAI-ARIA 1.2 Combobox with Listbox Popup design pattern:
- Keyboard Interaction:
- ↓ opens the popup listbox and focuses the first item.
- ↑ and ↓ navigate between matching suggestions.
- Enter commits the highlighted suggestion into the input and closes the popup.
- Esc closes the dropdown without modifying the input or clears the field.
- Alt + ↓ or clicking the disclosure button toggles the dropdown list.
- ARIA Structure:
- The text input has
role="combobox",aria-autocomplete="list",aria-expanded="false|true", andaria-controlsreferencing the popup listbox. aria-activedescendantcommunicates the currently focused suggestion to assistive software without moving DOM focus out of the input.
- The text input has
- Screen Reader Support: When options filter as the user types, screen readers announce the number of matching items automatically.
Keyboard Interactions
| Key | Action |
|---|---|
| ↓ | Opens popup listbox or navigates to the next option. |
| ↑ | Navigates to the previous option in the listbox. |
| Enter | Selects the active option and closes the dropdown list. |
| Esc | Closes the filtered popup listbox and retains input text. |
| Tab | Selects active match or moves focus to the next field. |
WAI-ARIA & Data Attributes
| Attribute | Target | Description |
|---|---|---|
[data-hovered] | Input, Item | Applied when pointer hovers over the input or item. |
[data-focused] | Input, Item | Active when the element holds keyboard focus. |
[data-selected] | Item | Present when an option is the current selected value. |
[data-disabled] | Input, Item | Present when interaction is disabled. |