{"slug":"combobox","title":"Combobox","description":"Using the combobox machine in your project.","contentType":"component","framework":"react","content":"A combobox is an input widget with an associated popup that enables users to\nselect a value from a collection of possible values.\n\n## Resources\n\n\n[Latest version: v1.31.0](https://www.npmjs.com/package/@zag-js/combobox)\n[Logic Visualizer](https://zag-visualizer.vercel.app/combobox)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/combobox)\n\n\n\n**Features**\n\n- Support for selecting multiple values\n- Support for disabled options\n- Support for custom user input values\n- Support for mouse, touch, and keyboard interactions\n- Keyboard support for opening the combo box list box using the arrow keys,\n  including automatically focusing the first or last item accordingly\n\n## Installation\n\nTo use the combobox machine in your project, run the following command in your\ncommand line:\n\n```bash\nnpm install @zag-js/combobox @zag-js/react\n# or\nyarn add @zag-js/combobox @zag-js/react\n```\n\n## Anatomy\n\nTo set up the combobox correctly, you'll need to understand its anatomy and how\nwe name its parts.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nFirst, import the combobox package into your project\n\n```jsx\nimport * as combobox from \"@zag-js/combobox\"\n```\n\nThe combobox package exports these functions:\n\n- `machine` — The state machine logic for the combobox widget.\n- `connect` — The function that translates the machine's state to JSX attributes\n  and event handlers.\n- `collection` - The function that creates a\n  [collection interface](/overview/collection) from an array of items.\n\nNext, import the required hooks and functions for your framework and use the\ncombobox machine in your project 🔥\n\n```jsx\nimport * as combobox from \"@zag-js/combobox\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\nimport { useState, useId } from \"react\"\n\nconst comboboxData = [\n  { label: \"Zambia\", code: \"ZA\" },\n  { label: \"Benin\", code: \"BN\" },\n  //...\n]\n\nexport function Combobox() {\n  const [options, setOptions] = useState(comboboxData)\n\n  const collection = combobox.collection({\n    items: options,\n    itemToValue: (item) => item.code,\n    itemToString: (item) => item.label,\n  })\n\n  const service = useMachine(combobox.machine, {\n    id: useId(),\n    collection,\n    onOpenChange() {\n      setOptions(comboboxData)\n    },\n    onInputValueChange({ inputValue }) {\n      const filtered = comboboxData.filter((item) =>\n        item.label.toLowerCase().includes(inputValue.toLowerCase()),\n      )\n      setOptions(filtered.length > 0 ? filtered : comboboxData)\n    },\n  })\n\n  const api = combobox.connect(service, normalizeProps)\n\n  return (\n    <div>\n      <div {...api.getRootProps()}>\n        <label {...api.getLabelProps()}>Select country</label>\n        <div {...api.getControlProps()}>\n          <input {...api.getInputProps()} />\n          <button {...api.getTriggerProps()}>▼</button>\n        </div>\n      </div>\n      <div {...api.getPositionerProps()}>\n        {options.length > 0 && (\n          <ul {...api.getContentProps()}>\n            {options.map((item) => (\n              <li key={item.code} {...api.getItemProps({ item })}>\n                {item.label}\n              </li>\n            ))}\n          </ul>\n        )}\n      </div>\n    </div>\n  )\n}\n```\n\n### Setting the initial value\n\nTo set the initial value of the combobox, pass the `defaultValue` property to\nthe machine's context.\n\n```jsx {13}\nconst collection = combobox.collection({\n  items: [\n    { label: \"Nigeria\", value: \"ng\" },\n    { label: \"Ghana\", value: \"gh\" },\n    { label: \"Kenya\", value: \"ke\" },\n    //...\n  ],\n})\n\nconst service = useMachine(combobox.machine, {\n  id: useId(),\n  collection,\n  defaultValue: [\"ng\"],\n})\n```\n\n### Controlled combobox\n\nTo control the value programmatically, pass the `value` and `onValueChange`\nproperties to the machine function.\n\n```tsx\nimport { useState } from \"react\"\n\nexport function ControlledCombobox() {\n  const [value, setValue] = useState([\"ng\"])\n\n  const service = useMachine(combobox.machine, {\n      value,\n      onValueChange(details) {\n        setValue(details.value)\n      }\n  })\n\n  return (\n    // ...\n    )\n}\n```\n\n### Selecting multiple values\n\nTo allow selecting multiple values, set the `multiple` property in the machine's\ncontext to `true`.\n\n```jsx {4}\nconst service = useMachine(combobox.machine, {\n  id: useId(),\n  collection,\n  multiple: true,\n})\n```\n\n### Using a custom object format\n\nBy default, the combobox collection expects an array of items with `label` and\n`value` properties. To use a custom object format, pass the `itemToString` and\n`itemToValue` properties to the collection function.\n\n- `itemToString` — A function that returns the string representation of an item.\n  Used to compare items when filtering.\n- `itemToValue` — A function that returns the unique value of an item.\n- `itemToDisabled` — A function that returns the disabled state of an item.\n\n```jsx\nconst collection = combobox.collection({\n  // custom object format\n  items: [\n    { id: 1, fruit: \"Banana\", available: true, quantity: 10 },\n    { id: 2, fruit: \"Apple\", available: false, quantity: 5 },\n    { id: 3, fruit: \"Orange\", available: true, quantity: 3 },\n    //...\n  ],\n  // convert item to string\n  itemToString(item) {\n    return item.fruit\n  },\n  // convert item to value\n  itemToValue(item) {\n    return item.id\n  },\n  // convert item to disabled state\n  itemToDisabled(item) {\n    return !item.available || item.quantity === 0\n  },\n})\n\n// use the collection\nconst service = useMachine(combobox.machine, {\n  id: useId(),\n  collection,\n})\n```\n\n### Rendering the selected values outside the combobox\n\nBy default, the selected values of a combobox are displayed in the input\nelement, when selecting multiple items, it is a better UX to render the selected\nvalue outside the combobox.\n\nTo achieve this you need to:\n\n- Set the `selectionBehavior` to `clear`, which clears the input value when an\n  item is selected.\n- Set the `multiple` property to `true` to allow selecting multiple values.\n- Render the selected values outside the combobox.\n\n```jsx {4-6}\nconst service = useMachine(combobox.machine, {\n  id: useId(),\n  collection,\n  selectionBehavior: \"clear\",\n  multiple: true,\n})\n```\n\n### Disabling the combobox\n\nTo make a combobox disabled, set the context's `disabled` property to `true`\n\n```jsx {2}\nconst service = useMachine(combobox.machine, {\n  disabled: true,\n})\n```\n\n### Disabling an option\n\nTo make a combobox option disabled, pass the `isItemDisabled` property to the\ncollection function.\n\n```jsx {6-8}\nconst service = useMachine(combobox.machine, {\n  id: useId(),\n  collection: combobox.collection({\n    items: countries,\n    isItemDisabled(item) {\n      return item.disabled\n    },\n  }),\n})\n```\n\n### Close on select\n\nThis behaviour ensures that the menu is closed when an option is selected and is\n`true` by default. It's only concerned with when an option is selected with\npointer or enter key. To disable the behaviour, set the `closeOnSelect` property\nin the machine's context to `false`.\n\n```jsx {2}\nconst service = useMachine(combobox.machine, {\n  closeOnSelect: false,\n})\n```\n\n### Making the combobox readonly\n\nTo make a combobox readonly, set the context's `readOnly` property to `true`\n\n```jsx {2}\nconst service = useMachine(combobox.machine, {\n  readOnly: true,\n})\n```\n\n### Listening for highlight changes\n\nWhen an option is highlighted with the pointer or keyboard, use the\n`onHighlightChange` property to listen for this change and do something with it.\n\n```jsx {3-6}\nconst service = useMachine(combobox.machine, {\n  id: useId(),\n  onHighlightChange(details) {\n    // details => { value: string | null; item: CollectionItem | null }\n    console.log(details)\n  },\n})\n```\n\n### Listening for value changes\n\nWhen an item is selected, use `onValueChange` property to listen for this change\nand do something with it.\n\n```jsx {3-6}\nconst service = useMachine(combobox.machine, {\n  onValueChange(details) {\n    // details => { value: string[]; items: CollectionItem[] }\n    console.log(details)\n  },\n})\n```\n\n### Usage within forms\n\nThe combobox works when placed within a form and the form is submitted. We\nachieve this by:\n\n- ensuring we emit the input event as the value changes.\n- adding a `name` attribute to the input so the value can be accessed in the\n  `FormData`.\n\nTo get this feature working you need to pass a `name` option to the context.\n\n```jsx {2}\nconst service = useMachine(combobox.machine, {\n  name: \"countries\",\n})\n```\n\n### Allowing custom values\n\nBy default, the combobox only allows selecting values from the collection. To\nallow custom values, set the `allowCustomValue` property in the machine's\ncontext to `true`.\n\n```jsx {2}\nconst service = useMachine(combobox.machine, {\n  allowCustomValue: true,\n})\n```\n\n## Styling guide\n\nEarlier, we mentioned that each combobox part has a `data-part` attribute added\nto them to select and style them in the DOM.\n\n### Open and closed state\n\nWhen the combobox is open or closed, the `data-state` attribute is added to the\ncontent,control, input and control parts.\n\n```css\n[data-part=\"control\"][data-state=\"open|closed\"] {\n  /* styles for control open or state */\n}\n\n[data-part=\"input\"][data-state=\"open|closed\"] {\n  /* styles for control open or state */\n}\n\n[data-part=\"trigger\"][data-state=\"open|closed\"] {\n  /* styles for control open or state */\n}\n\n[data-part=\"content\"][data-state=\"open|closed\"] {\n  /* styles for control open or state */\n}\n```\n\n### Focused State\n\nWhen the combobox is focused, the `data-focus` attribute is added to the control\nand label parts.\n\n```css\n[data-part=\"control\"][data-focus] {\n  /* styles for control focus state */\n}\n\n[data-part=\"label\"][data-focus] {\n  /* styles for label focus state */\n}\n```\n\n### Disabled State\n\nWhen the combobox is disabled, the `data-disabled` attribute is added to the\nlabel, control, trigger and option parts.\n\n```css\n[data-part=\"label\"][data-disabled] {\n  /* styles for label disabled state */\n}\n\n[data-part=\"control\"][data-disabled] {\n  /* styles for control disabled state */\n}\n\n[data-part=\"trigger\"][data-disabled] {\n  /* styles for trigger disabled state */\n}\n\n[data-part=\"item\"][data-disabled] {\n  /* styles for item disabled state */\n}\n```\n\n### Invalid State\n\nWhen the combobox is invalid, the `data-invalid` attribute is added to the root,\nlabel, control and input parts.\n\n```css\n[data-part=\"root\"][data-invalid] {\n  /* styles for root invalid state */\n}\n\n[data-part=\"label\"][data-invalid] {\n  /* styles for label invalid state */\n}\n\n[data-part=\"control\"][data-invalid] {\n  /* styles for control invalid state */\n}\n\n[data-part=\"input\"][data-invalid] {\n  /* styles for input invalid state */\n}\n```\n\n### Selected State\n\nWhen a combobox item is selected, the `data-state` attribute is added to the\nitem part.\n\n```css\n[data-part=\"item\"][data-state=\"checked|unchecked\"] {\n  /* styles for item selected state */\n}\n```\n\n### Highlighted State\n\nWhen a combobox item is highlighted, the `data-highlighted` attribute is added\nto the item part.\n\n```css\n[data-part=\"item\"][data-highlighted] {\n  /* styles for item highlighted state */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe combobox machine exposes the following context properties:\n\n**`open`**\nType: `boolean`\nDescription: The controlled open state of the combobox\n\n**`defaultOpen`**\nType: `boolean`\nDescription: The initial open state of the combobox when rendered.\nUse when you don't need to control the open state of the combobox.\n\n**`ids`**\nType: `Partial<{ root: string; label: string; control: string; input: string; content: string; trigger: string; clearTrigger: string; item: (id: string, index?: number) => string; positioner: string; itemGroup: (id: string | number) => string; itemGroupLabel: (id: string | number) => string; }>`\nDescription: The ids of the elements in the combobox. Useful for composition.\n\n**`inputValue`**\nType: `string`\nDescription: The controlled value of the combobox's input\n\n**`defaultInputValue`**\nType: `string`\nDescription: The initial value of the combobox's input when rendered.\nUse when you don't need to control the value of the combobox's input.\n\n**`name`**\nType: `string`\nDescription: The `name` attribute of the combobox's input. Useful for form submission\n\n**`form`**\nType: `string`\nDescription: The associate form of the combobox.\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the combobox is disabled\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the combobox is readonly. This puts the combobox in a \"non-editable\" mode\nbut the user can still interact with it\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the combobox is invalid\n\n**`required`**\nType: `boolean`\nDescription: Whether the combobox is required\n\n**`placeholder`**\nType: `string`\nDescription: The placeholder text of the combobox's input\n\n**`defaultHighlightedValue`**\nType: `string`\nDescription: The initial highlighted value of the combobox when rendered.\nUse when you don't need to control the highlighted value of the combobox.\n\n**`highlightedValue`**\nType: `string`\nDescription: The controlled highlighted value of the combobox\n\n**`value`**\nType: `string[]`\nDescription: The controlled value of the combobox's selected items\n\n**`defaultValue`**\nType: `string[]`\nDescription: The initial value of the combobox's selected items when rendered.\nUse when you don't need to control the value of the combobox's selected items.\n\n**`inputBehavior`**\nType: `\"autohighlight\" | \"autocomplete\" | \"none\"`\nDescription: Defines the auto-completion behavior of the combobox.\n\n- `autohighlight`: The first focused item is highlighted as the user types\n- `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated\n\n**`selectionBehavior`**\nType: `\"clear\" | \"replace\" | \"preserve\"`\nDescription: The behavior of the combobox input when an item is selected\n\n- `replace`: The selected item string is set as the input value\n- `clear`: The input value is cleared\n- `preserve`: The input value is preserved\n\n**`autoFocus`**\nType: `boolean`\nDescription: Whether to autofocus the input on mount\n\n**`openOnClick`**\nType: `boolean`\nDescription: Whether to open the combobox popup on initial click on the input\n\n**`openOnChange`**\nType: `boolean | ((details: InputValueChangeDetails) => boolean)`\nDescription: Whether to show the combobox when the input value changes\n\n**`allowCustomValue`**\nType: `boolean`\nDescription: Whether to allow typing custom values in the input\n\n**`alwaysSubmitOnEnter`**\nType: `boolean`\nDescription: Whether to always submit on Enter key press, even if popup is open.\nUseful for single-field autocomplete forms where Enter should submit the form.\n\n**`loopFocus`**\nType: `boolean`\nDescription: Whether to loop the keyboard navigation through the items\n\n**`positioning`**\nType: `PositioningOptions`\nDescription: The positioning options to dynamically position the menu\n\n**`onInputValueChange`**\nType: `(details: InputValueChangeDetails) => void`\nDescription: Function called when the input's value changes\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails<T>) => void`\nDescription: Function called when a new item is selected\n\n**`onHighlightChange`**\nType: `(details: HighlightChangeDetails<T>) => void`\nDescription: Function called when an item is highlighted using the pointer\nor keyboard navigation.\n\n**`onSelect`**\nType: `(details: SelectionDetails) => void`\nDescription: Function called when an item is selected\n\n**`onOpenChange`**\nType: `(details: OpenChangeDetails) => void`\nDescription: Function called when the popup is opened\n\n**`translations`**\nType: `IntlTranslations`\nDescription: Specifies the localized strings that identifies the accessibility elements and their states\n\n**`collection`**\nType: `ListCollection<T>`\nDescription: The collection of items\n\n**`multiple`**\nType: `boolean`\nDescription: Whether to allow multiple selection.\n\n**Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`.\nIt is recommended to render the selected items in a separate container.\n\n**`closeOnSelect`**\nType: `boolean`\nDescription: Whether to close the combobox when an item is selected.\n\n**`openOnKeyPress`**\nType: `boolean`\nDescription: Whether to open the combobox on arrow key press\n\n**`scrollToIndexFn`**\nType: `(details: ScrollToIndexDetails) => void`\nDescription: Function to scroll to a specific index\n\n**`composite`**\nType: `boolean`\nDescription: Whether the combobox is a composed with other composite widgets like tabs\n\n**`disableLayer`**\nType: `boolean`\nDescription: Whether to disable registering this a dismissable layer\n\n**`navigate`**\nType: `(details: NavigateDetails) => void`\nDescription: Function to navigate to the selected item\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n**`onPointerDownOutside`**\nType: `(event: PointerDownOutsideEvent) => void`\nDescription: Function called when the pointer is pressed down outside the component\n\n**`onFocusOutside`**\nType: `(event: FocusOutsideEvent) => void`\nDescription: Function called when the focus is moved outside the component\n\n**`onInteractOutside`**\nType: `(event: InteractOutsideEvent) => void`\nDescription: Function called when an interaction happens outside the component\n\n### Machine API\n\nThe combobox `api` exposes the following methods:\n\n**`focused`**\nType: `boolean`\nDescription: Whether the combobox is focused\n\n**`open`**\nType: `boolean`\nDescription: Whether the combobox is open\n\n**`inputValue`**\nType: `string`\nDescription: The value of the combobox input\n\n**`highlightedValue`**\nType: `string`\nDescription: The value of the highlighted item\n\n**`highlightedItem`**\nType: `V`\nDescription: The highlighted item\n\n**`setHighlightValue`**\nType: `(value: string) => void`\nDescription: The value of the combobox input\n\n**`clearHighlightValue`**\nType: `VoidFunction`\nDescription: Function to clear the highlighted value\n\n**`syncSelectedItems`**\nType: `VoidFunction`\nDescription: Function to sync the selected items with the value.\nUseful when `value` is updated from async sources.\n\n**`selectedItems`**\nType: `V[]`\nDescription: The selected items\n\n**`hasSelectedItems`**\nType: `boolean`\nDescription: Whether there's a selected item\n\n**`value`**\nType: `string[]`\nDescription: The selected item keys\n\n**`valueAsString`**\nType: `string`\nDescription: The string representation of the selected items\n\n**`selectValue`**\nType: `(value: string) => void`\nDescription: Function to select a value\n\n**`setValue`**\nType: `(value: string[]) => void`\nDescription: Function to set the value of the combobox\n\n**`clearValue`**\nType: `(value?: string) => void`\nDescription: Function to clear the value of the combobox\n\n**`focus`**\nType: `VoidFunction`\nDescription: Function to focus on the combobox input\n\n**`setInputValue`**\nType: `(value: string, reason?: InputValueChangeReason) => void`\nDescription: Function to set the input value of the combobox\n\n**`getItemState`**\nType: `(props: ItemProps) => ItemState`\nDescription: Returns the state of a combobox item\n\n**`setOpen`**\nType: `(open: boolean, reason?: OpenChangeReason) => void`\nDescription: Function to open or close the combobox\n\n**`collection`**\nType: `ListCollection<V>`\nDescription: Function to toggle the combobox\n\n**`reposition`**\nType: `(options?: Partial<PositioningOptions>) => void`\nDescription: Function to set the positioning options\n\n**`multiple`**\nType: `boolean`\nDescription: Whether the combobox allows multiple selections\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the combobox is disabled\n\n### Data Attributes\n\n**`Root`**\n\n**`data-scope`**: combobox\n**`data-part`**: root\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n\n**`Label`**\n\n**`data-scope`**: combobox\n**`data-part`**: label\n**`data-readonly`**: Present when read-only\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-required`**: Present when required\n**`data-focus`**: Present when focused\n\n**`Control`**\n\n**`data-scope`**: combobox\n**`data-part`**: control\n**`data-state`**: \"open\" | \"closed\"\n**`data-focus`**: Present when focused\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n\n**`Input`**\n\n**`data-scope`**: combobox\n**`data-part`**: input\n**`data-invalid`**: Present when invalid\n**`data-autofocus`**: \n**`data-state`**: \"open\" | \"closed\"\n\n**`Trigger`**\n\n**`data-scope`**: combobox\n**`data-part`**: trigger\n**`data-state`**: \"open\" | \"closed\"\n**`data-invalid`**: Present when invalid\n**`data-focusable`**: \n**`data-readonly`**: Present when read-only\n**`data-disabled`**: Present when disabled\n\n**`Content`**\n\n**`data-scope`**: combobox\n**`data-part`**: content\n**`data-state`**: \"open\" | \"closed\"\n**`data-nested`**: listbox\n**`data-has-nested`**: listbox\n**`data-placement`**: The placement of the content\n**`data-empty`**: Present when the content is empty\n\n**`List`**\n\n**`data-scope`**: combobox\n**`data-part`**: list\n**`data-empty`**: Present when the content is empty\n\n**`ClearTrigger`**\n\n**`data-scope`**: combobox\n**`data-part`**: clear-trigger\n**`data-invalid`**: Present when invalid\n\n**`Item`**\n\n**`data-scope`**: combobox\n**`data-part`**: item\n**`data-highlighted`**: Present when highlighted\n**`data-state`**: \"checked\" | \"unchecked\"\n**`data-disabled`**: Present when disabled\n**`data-value`**: The value of the item\n\n**`ItemText`**\n\n**`data-scope`**: combobox\n**`data-part`**: item-text\n**`data-state`**: \"checked\" | \"unchecked\"\n**`data-disabled`**: Present when disabled\n**`data-highlighted`**: Present when highlighted\n\n**`ItemIndicator`**\n\n**`data-scope`**: combobox\n**`data-part`**: item-indicator\n**`data-state`**: \"checked\" | \"unchecked\"\n\n**`ItemGroup`**\n\n**`data-scope`**: combobox\n**`data-part`**: item-group\n**`data-empty`**: Present when the content is empty\n\n### CSS Variables\n\n<CssVarTable name=\"combobox\" />\n\n## Accessibility\n\nAdheres to the\n[Combobox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).\n\n### Keyboard Interactions\n\n**`ArrowDown`**\nDescription: When the combobox is closed, opens the listbox and highlights to the first option.\nWhen the combobox is open, moves focus to the next option.\n\n**`ArrowUp`**\nDescription: When the combobox is closed, opens the listbox and highlights to the last option.\nWhen the combobox is open, moves focus to the previous option.\n\n**`Home`**\nDescription: When the combobox is open, moves focus to the first option.\n\n**`End`**\nDescription: When the combobox is open, moves focus to the last option.\n\n**`Escape`**\nDescription: Closes the listbox.\n\n**`Enter`**\nDescription: Selects the highlighted option and closes the combobox.\n\n**`Esc`**\nDescription: Closes the combobox","package":"@zag-js/combobox","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/combobox.mdx"}