# ComboBox

A combobox component that combines a text input with a dropdown list, allowing users to either select from predefined options or enter custom values. It features automatic filtering, keyboard navigation, and supports both controlled and uncontrolled modes.

## When to Use

- When users need to select from a large list of options and benefit from filtering by typing
- When you want to provide suggestions while allowing custom input values
- When you need a searchable dropdown with keyboard navigation support
- When the list of options is dynamic and can be filtered based on user input
- When you need a more flexible alternative to standard Select components
- When you want the selected item to appear first in the dropdown (using `items` prop with `sortSelectedToTop`)

## Component

---

### Properties

- **`children`** `ReactNode` — ComboBox.Item elements that define the available options
- **`placeholder`** `string` — Placeholder text when input is empty
- **`icon`** `ReactNode` — Icon element rendered before the input
- **`inputValue`** `string` — The current text value in controlled mode
- **`defaultInputValue`** `string` — The default text value in uncontrolled mode
- **`description`** `string` — Description text displayed below the input
- **`message`** `string` — Message text displayed below the input (validation message)
- **`selectedKey`** `string` — The currently selected key (controlled)
- **`defaultSelectedKey`** `string` — The key of the initially selected item (uncontrolled)
- **`allowsCustomValue`** `boolean` (default: `false`) — Whether the combo box allows custom values to be entered
- **`shouldCommitOnBlur`** `boolean` (default: `true`) — Whether to commit custom value on blur in allowsCustomValue mode
- **`clearOnBlur`** `boolean` (default: `false`) — Whether to clear selection and input on blur (only applies to non-custom-value mode)
- **`isClearable`** `boolean` (default: `false`) — Whether the combo box is clearable using ESC keyboard button or clear button inside the input
- **`sortSelectedToTop`** `boolean` (default: `true when items provided, false otherwise`) — Whether to sort selected item to the top when popover opens (only works with items prop)
- **`popoverTrigger`** `'focus' | 'input' | 'manual'` (default: `input`) — How the popover is triggered
- **`filter`** `((textValue: string, inputValue: string) => boolean) | false` — Custom filter function or false to disable filtering. If not provided, uses default contains filter
- **`hideTrigger`** `boolean` (default: `false`) — Whether to hide the trigger button
- **`size`** `'small' | 'medium' | 'large'` (default: `medium`) — ComboBox size
- **`necessityIndicator`** `'label' | 'icon'` — How the required state should be indicated
- **`onSelectionChange`** `(key: Key | null) => void` — Callback fired when the selected option changes
- **`onInputChange`** `(value: string) => void` — Callback fired when the input text changes
- **`onOpenChange`** `(isOpen: boolean) => void` — Callback fired when the popover opens or closes
- **`prefix`** `ReactNode` — Content rendered before the input
- **`suffix`** `ReactNode` — Content rendered after the input
- **`suffixPosition`** `'before' | 'after'` (default: `before`) — Position of suffix relative to validation/loading icons
- **`onClear`** `() => void` — Callback fired when the clear action is triggered
- **`direction`** `'top' | 'bottom'` (default: `bottom`) — Preferred direction for the dropdown
- **`shouldFlip`** `boolean` (default: `true`) — Whether the dropdown should flip to fit in viewport
- **`overlayOffset`** `number` (default: `8`) — Distance between input and dropdown in pixels
- **`items`** `Iterable<T>` — Items for the listbox (alternative to children, enables data-driven mode)
- **`disabledKeys`** `Iterable<Key>` — Keys of disabled items that cannot be selected
- **`containerPadding`** `number` (default: `8`) — Minimum padding in pixels between the popover and viewport edges
- **`onFocus`** `() => void` — Callback fired when focus enters the ComboBox
- **`onBlur`** `() => void` — Callback fired when focus leaves the ComboBox
- **`onKeyDown`** `(e: KeyboardEvent) => void` — Callback fired when a key is pressed on the input

#### Notable Properties

**defaultInputValue** - Sets the initial input value in uncontrolled mode. This is useful when you want to display a specific text in the input field on mount without selecting any option. When both `defaultInputValue` and `defaultSelectedKey` are provided, `defaultInputValue` takes priority. The value can be changed by the user and is not enforced after initial render.

**sortSelectedToTop** - When using the `items` prop (data-driven mode), the selected item will automatically appear at the top of the list when the popover opens. This is enabled by default when using `items`, but disabled when using JSX children. Set to `false` to maintain the original order.

**allowsCustomValue** - Enables users to enter values that aren't in the predefined options list. Custom values can be committed by pressing Enter or on blur (controlled by `shouldCommitOnBlur`). **Important**: When enabled, typing in the input will clear the current selection. When disabled (default), typing only filters the options without clearing the selection.

**shouldCommitOnBlur** - When `true` (default), custom values are committed when the input loses focus. When `false`, custom values only commit when Enter is pressed. Only applies when `allowsCustomValue` is enabled.

**clearOnBlur** - When `true`, clears both the selection and input when the input loses focus. When `false` (default), the input reverts to showing the current selection. Only applies to non-custom-value mode. Useful for search-like scenarios where you want to reset after each interaction.

**popoverTrigger** - Controls how the popover opens:
- `manual` - Only opens via trigger button or arrow keys
- `input` (default) - Opens when user types + manual triggers
- `focus` - Opens on input focus + typing + manual triggers

**filter** - Custom filter function or `false` to disable filtering. By default, uses a contains-based filter. Provide a custom function `(textValue, inputValue) => boolean` to customize filtering logic.

### Base Properties

Supports [Base properties](../../BaseProperties.md)

### Field Properties

Supports all [Field properties](../../FieldProperties.md)

### Styling Properties

#### styles

Customizes the root wrapper element of the ComboBox (the input container).

**Sub-elements:**
- `Prefix` - Container for prefix content and icons
- `Suffix` - Container for suffix content, validation icons, and action buttons
- `InputIcon` - Container for the left icon
- `ValidationIcon` - Container for validation state icons
- `State` - Container for state indicators

#### inputStyles

Customizes the text input element where users type.

#### triggerStyles

Customizes the dropdown trigger button that opens/closes the popover.

#### listBoxStyles

Customizes the dropdown list container that displays options.

#### overlayStyles

Customizes the popover overlay wrapper that contains the list.

#### optionStyles

Customizes individual option items in the dropdown list.

#### sectionStyles

Customizes section containers when using grouped options.

#### headingStyles

Customizes section heading elements within grouped options.

### Style Properties

These properties allow direct style application without using the `styles` prop:

- **Base:** `display`, `font`, `preset`, `hide`, `whiteSpace`, `opacity`, `transition`
- **Position:** `gridArea`, `order`, `gridColumn`, `gridRow`, `placeSelf`, `alignSelf`, `justifySelf`, `zIndex`, `margin`, `inset`, `position`
- **Dimension:** `width`, `height`, `flexBasis`, `flexGrow`, `flexShrink`, `flex`
- **Block:** `border`, `radius`, `shadow`, `outline`
- **Color:** `color`, `fill`, `fade`, `image`

### Modifiers

The `mods` property accepts the following modifiers you can override:

- **`invalid`** `boolean` — Applied when validation state is invalid
- **`valid`** `boolean` — Applied when validation state is valid
- **`disabled`** `boolean` — Applied when the combobox is disabled
- **`focused`** `boolean` — Applied when the input has focus
- **`loading`** `boolean` — Applied when the combobox is in loading state
- **`prefix`** `boolean` — Applied when prefix content is present
- **`suffix`** `boolean` — Applied when suffix content is present
- **`clearable`** `boolean` — Applied when the clear button is visible

## Variants

### Sizes

- `small` - Compact size for dense interfaces
- `medium` - Default size (default)
- `large` - Emphasized size for important inputs

### Popover Trigger Modes

- `manual` - Only opens via trigger button or arrow keys
- `input` - Opens when user types + manual triggers (default)
- `focus` - Opens on input focus + typing + manual triggers

### Validation States

- `valid` - Indicates successful validation
- `invalid` - Indicates validation errors

## Examples

### Basic Usage

```jsx
<ComboBox label="Fruit" placeholder="Select a fruit...">
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
  <ComboBox.Item key="cherry">Cherry</ComboBox.Item>
</ComboBox>
```

### With Default Selected Value

```jsx
<ComboBox
  label="Fruit"
  placeholder="Select a fruit..."
  defaultSelectedKey="banana"
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
  <ComboBox.Item key="cherry">Cherry</ComboBox.Item>
</ComboBox>
```

### Controlled Mode

```jsx
const [selectedKey, setSelectedKey] = useState('apple');

<ComboBox
  label="Fruit"
  selectedKey={selectedKey}
  onSelectionChange={setSelectedKey}
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
  <ComboBox.Item key="cherry">Cherry</ComboBox.Item>
</ComboBox>
```

### With Custom Values

```jsx
const [inputValue, setInputValue] = useState('');

<ComboBox
  allowsCustomValue
  label="Tags"
  placeholder="Type or select..."
  inputValue={inputValue}
  onInputChange={setInputValue}
>
  <ComboBox.Item key="react">React</ComboBox.Item>
  <ComboBox.Item key="vue">Vue</ComboBox.Item>
  <ComboBox.Item key="angular">Angular</ComboBox.Item>
</ComboBox>
```

### Clearable

```jsx
<ComboBox
  isClearable
  label="Fruit"
  placeholder="Select a fruit..."
  defaultSelectedKey="apple"
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
  <ComboBox.Item key="cherry">Cherry</ComboBox.Item>
</ComboBox>
```

### Clear on Blur

```jsx
const [selectedKey, setSelectedKey] = useState(null);

<ComboBox
  clearOnBlur
  label="Fruit"
  placeholder="Select a fruit..."
  selectedKey={selectedKey}
  onSelectionChange={setSelectedKey}
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
  <ComboBox.Item key="cherry">Cherry</ComboBox.Item>
</ComboBox>
```

### With Sections

```jsx
<ComboBox label="Food" placeholder="Select food...">
  <ComboBox.Section key="fruits" title="Fruits">
    <ComboBox.Item key="apple">Apple</ComboBox.Item>
    <ComboBox.Item key="banana">Banana</ComboBox.Item>
  </ComboBox.Section>
  <ComboBox.Section key="vegetables" title="Vegetables">
    <ComboBox.Item key="carrot">Carrot</ComboBox.Item>
    <ComboBox.Item key="broccoli">Broccoli</ComboBox.Item>
  </ComboBox.Section>
</ComboBox>
```

### With Sections (Dynamic)

Sections and items can be defined dynamically using the `items` prop with a render function. Pass hierarchical data where each section contains a `children` array.

```jsx
const categories = [
  { name: 'Fruits', children: [
    { key: 'apple', label: 'Apple' },
    { key: 'banana', label: 'Banana' },
  ]},
  { name: 'Vegetables', children: [
    { key: 'carrot', label: 'Carrot' },
    { key: 'broccoli', label: 'Broccoli' },
  ]},
];

<ComboBox label="Food" placeholder="Select food..." items={categories}>
  {(category) => (
    <ComboBox.Section key={category.name} title={category.name} items={category.children}>
      {(item) => <ComboBox.Item key={item.key}>{item.label}</ComboBox.Item>}
    </ComboBox.Section>
  )}
</ComboBox>
```

### With Disabled Items

```jsx
<ComboBox
  label="Fruit"
  placeholder="Select a fruit..."
  disabledKeys={['banana', 'date']}
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
  <ComboBox.Item key="cherry">Cherry</ComboBox.Item>
  <ComboBox.Item key="date">Date</ComboBox.Item>
</ComboBox>
```

### Loading State

```jsx
<ComboBox isLoading label="Fruit" placeholder="Select a fruit...">
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### Disabled State

```jsx
<ComboBox
  isDisabled
  label="Fruit"
  defaultSelectedKey="apple"
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### With Items Array

```jsx
const fruits = [
  { key: 'apple', label: 'Apple' },
  { key: 'banana', label: 'Banana' },
  { key: 'cherry', label: 'Cherry' },
];

<ComboBox label="Fruit" placeholder="Select..." items={fruits}>
  {(item) => <ComboBox.Item key={item.key}>{item.label}</ComboBox.Item>}
</ComboBox>
```

### Custom Filter Function

```jsx
<ComboBox
  label="Fruit"
  placeholder="Type to filter..."
  filter={(textValue, inputValue) => {
    return textValue.toLowerCase().startsWith(inputValue.toLowerCase());
  }}
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="apricot">Apricot</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### Disable Filtering

```jsx
<ComboBox
  label="Fruit"
  placeholder="Type anything..."
  filter={false}
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### Required Field

```jsx
<ComboBox
  isRequired
  label="Fruit"
  placeholder="Select a fruit..."
  necessityIndicator="label"
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### With Description

```jsx
<ComboBox
  label="Fruit"
  placeholder="Select a fruit..."
  description="Choose your favorite fruit from the list"
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### Hidden Trigger Button

```jsx
<ComboBox
  hideTrigger
  label="Fruit"
  placeholder="Type to search..."
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### Long List with Filtering

### Virtualized List

For large datasets, use the `items` prop with a render function. This enables automatic virtualization — only visible items are rendered in the DOM, providing smooth scrolling even with thousands of items. Virtualization is disabled when sections are present.

```jsx
const items = Array.from({ length: 1000 }, (_, i) => ({
  key: `item-${i}`,
  name: `Item ${i + 1}`,
}));

<ComboBox
  label="Virtualized List"
  placeholder="Search from 1000 items..."
  items={items}
>
  {(item) => <ComboBox.Item key={item.key}>{item.name}</ComboBox.Item>}
</ComboBox>
```

### Sort Selected Item to Top

```jsx
const fruits = [
  { key: 'apple', label: 'Apple' },
  { key: 'banana', label: 'Banana' },
  { key: 'cherry', label: 'Cherry' },
];

// Default behavior with items prop - selected item appears first
<ComboBox
  label="With sorting (default)"
  defaultSelectedKey="cherry"
  items={fruits}
>
  {(item) => <ComboBox.Item key={item.key}>{item.label}</ComboBox.Item>}
</ComboBox>

// Disable sorting to maintain original order
<ComboBox
  label="Without sorting"
  defaultSelectedKey="cherry"
  sortSelectedToTop={false}
  items={fruits}
>
  {(item) => <ComboBox.Item key={item.key}>{item.label}</ComboBox.Item>}
</ComboBox>
```

### Independent Input and Selection Control

```jsx
const [inputValue, setInputValue] = useState('');
const [selectedKey, setSelectedKey] = useState(null);

<ComboBox
  label="Fruit"
  inputValue={inputValue}
  selectedKey={selectedKey}
  onInputChange={setInputValue}
  onSelectionChange={setSelectedKey}
>
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

### Custom Value Without Commit on Blur

```jsx
<ComboBox
  allowsCustomValue
  shouldCommitOnBlur={false}
  label="Tags (Enter only)"
  placeholder="Type or select..."
>
  <ComboBox.Item key="react">React</ComboBox.Item>
  <ComboBox.Item key="vue">Vue</ComboBox.Item>
</ComboBox>
```

### Show All Items When No Results

```jsx
{/* Type text that yields no results, then click trigger or press arrow keys */}
<ComboBox label="Search Fruits" placeholder="Type 'xyz' to see behavior...">
  <ComboBox.Item key="apple">Apple</ComboBox.Item>
  <ComboBox.Item key="banana">Banana</ComboBox.Item>
</ComboBox>
```

## Accessibility

### Keyboard Navigation

- `Tab` - Moves focus to/from the combobox input
- `ArrowDown` - Opens popover (if closed) or moves focus to next option
- `ArrowUp` - Opens popover (if closed) or moves focus to previous option
- `Enter` - Selects the focused option and closes popover
- `Escape` - Closes popover (if open) or clears input (if clearable and popover closed)
- `Home` - Moves focus to the first option (when popover is open)
- `End` - Moves focus to the last option (when popover is open)
- Typing - Filters options based on input value

### Screen Reader Support

- Input announces as "combobox" with appropriate expanded/collapsed state
- Selected option is announced when selection changes
- Focused option is announced as user navigates with arrow keys
- Loading state is announced via loading indicator
- Validation states are announced through validation icons
- Clear button announces as "Clear" action

### ARIA Properties

- `role="combobox"` - Identifies the input as a combobox
- `aria-expanded` - Indicates whether the popover is open or closed
- `aria-haspopup="listbox"` - Indicates the combobox has a listbox popup
- `aria-controls` - References the listbox ID when popover is open
- `aria-activedescendant` - References the currently focused option
- `aria-label` - Provides accessible label when no visible label exists
- `aria-required` - Indicates required fields
- `aria-invalid` - Indicates validation errors
- `aria-describedby` - Links to description and error messages

## Best Practices

1. **Do**: Provide clear labels and placeholders
   ```jsx
   <ComboBox
     label="Country"
     placeholder="Search countries..."
   >
     <ComboBox.Item key="us">United States</ComboBox.Item>
   </ComboBox>
   ```

2. **Do**: Use sections to group related options
   ```jsx
   <ComboBox label="Contact">
     <ComboBox.Section title="Recent">
       <ComboBox.Item key="john">John Doe</ComboBox.Item>
     </ComboBox.Section>
     <ComboBox.Section title="All Contacts">
       <ComboBox.Item key="jane">Jane Smith</ComboBox.Item>
     </ComboBox.Section>
   </ComboBox>
   ```

3. **Do**: Enable `allowsCustomValue` when user input flexibility is needed
   ```jsx
   <ComboBox
     allowsCustomValue
     label="Email"
     placeholder="Enter or select email..."
   >
     <ComboBox.Item key="work">work@example.com</ComboBox.Item>
   </ComboBox>
   ```

4. **Don't**: Use ComboBox for a small fixed set of options
   ```jsx
   {/* Use Select instead for simple dropdowns */}
   <ComboBox label="Yes or No">
     <ComboBox.Item key="yes">Yes</ComboBox.Item>
     <ComboBox.Item key="no">No</ComboBox.Item>
   </ComboBox>
   ```

5. **Don't**: Disable filtering on large lists
   ```jsx
   {/* Defeats the purpose of ComboBox's search capability */}
   <ComboBox filter={false} label="Countries">
     {/* 200+ countries... */}
   </ComboBox>
   ```

6. **Accessibility**: Always provide meaningful labels and descriptions
   ```jsx
   <ComboBox
     label="Product Category"
     description="Select the primary category for your product"
     isRequired
   >
     <ComboBox.Item key="electronics">Electronics</ComboBox.Item>
   </ComboBox>
   ```

7. **Performance**: Use `items` prop with render function for dynamic lists
   ```jsx
   <ComboBox items={largeDataset}>
     {(item) => <ComboBox.Item key={item.id}>{item.name}</ComboBox.Item>}
   </ComboBox>
   ```

8. **Smart Behavior**: The ComboBox automatically shows all items when opening the popover after filtering yields no results
   ```jsx
   {/* User types "xyz" (no match), then clicks trigger - shows all items */}
   <ComboBox label="Search">
     <ComboBox.Item key="apple">Apple</ComboBox.Item>
     <ComboBox.Item key="banana">Banana</ComboBox.Item>
   </ComboBox>
   ```

9. **Selected Item Sorting**: Use `items` prop for automatic selected-item-first sorting
   ```jsx
   {/* With items prop, selected item appears first by default */}
   <ComboBox items={options} defaultSelectedKey="option5">
     {(item) => <ComboBox.Item key={item.key}>{item.label}</ComboBox.Item>}
   </ComboBox>
   ```

10. **Independent Control**: Control input and selection separately when needed
    ```jsx
    const [inputValue, setInputValue] = useState('');
    const [selectedKey, setSelectedKey] = useState(null);
    
    <ComboBox
      inputValue={inputValue}
      selectedKey={selectedKey}
      onInputChange={setInputValue}
      onSelectionChange={setSelectedKey}
    >
      {/* options */}
    </ComboBox>
    ```

## Integration with Forms

This component supports all [Field properties](../../FieldProperties.md) when used within a Form. It integrates seamlessly with the form validation system and supports both controlled and uncontrolled modes.

### Form Example

```jsx
<Form>
  <ComboBox
    name="country"
    label="Country"
    isRequired
    validationBehavior="native"
  >
    <ComboBox.Item key="us">United States</ComboBox.Item>
    <ComboBox.Item key="uk">United Kingdom</ComboBox.Item>
    <ComboBox.Item key="ca">Canada</ComboBox.Item>
  </ComboBox>
</Form>
```

## Suggested Improvements

- **Virtual Scrolling**: Add support for virtualizing long lists to improve performance with thousands of items
- **Multi-Select Mode**: Add ability to select multiple items with tag-based display
- **Async Loading**: Built-in support for async data loading with loading states per scroll position (partial support via `isLoading` prop)
- **Custom Option Rendering**: Allow more flexibility in rendering option content beyond text
- **Grouped Item Navigation**: Improve keyboard navigation to allow jumping between section groups (e.g., Ctrl+Arrow to skip to next section)
- **Empty State Component**: Allow passing custom React components for empty state when no results are found
- **Prefix/Suffix Slots**: Add more flexible slot-based API for prefix and suffix content
- **Item Descriptions**: Support for showing secondary text/descriptions in list items

## Related Components

- [Select](./Select.md) - Use when you need a simple dropdown without filtering
- [TextInput](./TextInput.md) - Use for free-form text input without suggestions
- [ListBox](./ListBox.md) - The underlying list component used in ComboBox popover
- [FilterPicker](./FilterPicker.md) - Alternative with different interaction patterns
