Combo Box
A searchable multi-select combo box with compound components. Use ComboBox.Composed for the simple flat-props API, or compose with Root, SearchTrigger, Content, Item, and Tags for full control over item rendering and tag display.
Installation
bunx @happlyui/cli@latest add combo-box
Dependencies
npm packages
@remixicon/react@radix-ui/react-scroll-areareact-remove-scroll
Registry dependencies
These are automatically installed when you add this component.
form-field-contextuse-form-field-bindingpopoverinputtaghapply-ui-utils
Usage
import * as ComboBox from "@/components/ui/combo-box"
// Simple usage
<ComboBox.Composed
options={options}
value={selected}
onValueChange={setSelected}
/>
// Compound usage
<ComboBox.Root options={options} value={selected} onValueChange={setSelected}>
<ComboBox.SearchTrigger />
<ComboBox.Content />
<ComboBox.Tags />
</ComboBox.Root>
Examples
Default
Basic multi-select combo box with label.
<ComboBox.Composed
options={options}
value={value}
onValueChange={setValue}
/>
With Pre-selected Values
Combo box with initial selections, useful for editing saved forms.
<ComboBox.Composed
options={options}
value={['ai', 'product', 'saas']}
onValueChange={setValue}
/>
Uncontrolled
Use defaultValue for uncontrolled mode in forms.
<ComboBox.Composed
options={options}
defaultValue={['grants', 'loans']}
name="sectors"
/>
Form Submission
Use the name prop to include selected values in form submissions via hidden inputs.
<ComboBox.Composed
options={options}
value={value}
onValueChange={setValue}
name="tags"
/>
Sizes
All three input sizes: medium (default), small, and xsmall.
<ComboBox.Composed options={options} value={value} onValueChange={setValue} />
<ComboBox.Composed options={options} size="small" />
<ComboBox.Composed options={options} size="xsmall" />
Selection Constraints
Limit selections with max, min, or both combined.
<ComboBox.Composed options={options} value={value} onValueChange={setValue} max={3} />
Tag Variants
Gray (default), stroke tag style, and selectAllLabel summary tag.
<ComboBox.Composed options={options} tagVariant="stroke" />
States
Error and disabled states.
<ComboBox.Composed options={options} hasError />
<ComboBox.Composed options={options} disabled />
Customization
Custom icon, placeholder text, and empty message.
<ComboBox.Composed options={options} icon={RiMapPinLine} placeholder="Search cities..." />
With Icons
Options with leading icons — supports React components and image URLs.
<ComboBox.Composed options={iconOptions} value={value} onValueChange={setValue} />
Compound Custom Items
Use the compound API for fully custom item rendering with checkbox indicators, avatars, and custom layouts.
<ComboBox.Root options={users} value={selected} onValueChange={setSelected}>
<ComboBox.SearchTrigger placeholder="Search team members..." />
<ComboBox.Content>
{filteredOptions.map(user => (
<ComboBox.Item key={user.value} value={user.value}>
<Checkbox checked={isSelected(user.value)} />
<Avatar src={user.avatar} />
<span>{user.name}</span>
<ComboBox.ItemIndicator selected={isSelected(user.value)} />
</ComboBox.Item>
))}
</ComboBox.Content>
<ComboBox.Tags variant="stroke" />
</ComboBox.Root>
Compound Custom Tags
Omit ComboBox.Tags and render your own tag display using useComboBoxContext().
<ComboBox.Root options={users} value={selected} onValueChange={setSelected}>
<ComboBox.SearchTrigger />
<ComboBox.Content />
<CustomTags />
</ComboBox.Root>
Preview
Preview mode renders the option list inline in the DOM and hides tags.
<ComboBox.Composed
options={options}
value={value}
onValueChange={setValue}
preview
/>
Custom Max Height
Override the dropdown height via the --combobox-content-max-height CSS variable on Content.
<ComboBox.Root options={options} value={value} onValueChange={setValue}>
<ComboBox.SearchTrigger />
<ComboBox.Content className="[--combobox-content-max-height:350px]" />
<ComboBox.Tags />
</ComboBox.Root>
API Reference
ComboBox.Root
Context provider and state orchestrator. Wraps all compound sub-components.
| Prop | Type | Default | Description |
|---|---|---|---|
options | ComboBoxOption[] | - | Available options ({ value, label, icon? }). |
value | string[] | - | Currently selected values (controlled). |
defaultValue | string[] | [] | Default selected values (uncontrolled). |
onValueChange | (values: string[], selectedOptions: ComboBoxOption[]) => void | - | Callback when selection changes. |
name | string | - | Form field name — renders hidden inputs for form submission. |
max | number | Infinity | Maximum number of selections. |
min | number | 0 | Minimum selections (prevents removing below this). |
disabled | boolean | false | Disabled state. |
hasError | boolean | false | Error state. |
size | 'medium' | 'small' | 'xsmall' | 'medium' | Input size. |
preview | boolean | false | Preview mode — renders the option list inline in the DOM and hides tags. |
onOpenChange | (open: boolean) => void | - | Callback when dropdown open state changes. |
ComboBox.SearchTrigger
Search input trigger with leading icon. Uses inputVariants for styling.
| Prop | Type | Default | Description |
|---|---|---|---|
leadingIcon | React.ElementType | RiSearchLine | Leading icon component. |
trailingIcon | React.ElementType | RiArrowDownSLine | Trailing icon component. |
placeholder | string | 'Choose or search...' | Search input placeholder. |
ComboBox.Content
Dropdown popover content with scroll area. When children are omitted, auto-renders items from filtered options.
| Prop | Type | Default | Description |
|---|---|---|---|
emptyMessage | string | 'No results found.' | Message when no options match the search. |
children | React.ReactNode | - | Custom content — overrides default item rendering. |
ComboBox.Item
A single option row. When children are provided, renders custom content; otherwise renders icon + label from options.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | The option value this item represents. |
children | React.ReactNode | - | Custom content for the item. |
ComboBox.ItemIcon
Polymorphic icon for items. Supports React components and image URL strings.
| Prop | Type | Default | Description |
|---|---|---|---|
as | React.ElementType | string | - | Icon component or image URL. |
ComboBox.ItemIndicator
Check indicator for selected items. Defaults to RiCheckLine.
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | - | Whether the item is selected. |
children | React.ReactNode | - | Custom indicator content. |
ComboBox.Empty
Empty state content shown when no options match.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | 'No results found.' | Custom empty message. |
ComboBox.Tags
Tag display area for selected items. Omit entirely for custom tag rendering.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'stroke' | 'gray' | 'gray' | Visual variant for tags. |
selectAllLabel | string | - | When provided and all options selected, show a single summary tag. |
ComboBox.Composed
Convenience flat-props wrapper. Assembles Root + SearchTrigger + Content + Tags internally. Drop-in replacement for the v2 API.
| Prop | Type | Default | Description |
|---|---|---|---|
icon | React.ElementType | RiSearchLine | Leading icon component. |
placeholder | string | 'Choose or search...' | Search input placeholder. |
emptyMessage | string | 'No results found.' | Message when no options match. |
selectAllLabel | string | - | Summary tag label when all selected. |
tagVariant | 'stroke' | 'gray' | 'gray' | Visual variant for tags. |
useComboBoxContext
Hook to access ComboBox state from custom child components. Returns options, value, filteredOptions, isSelected, toggle, remove, removeAll, search, setSearch, open, disabled, and more.
| Prop | Type | Default | Description |
|---|