Skip to main content

A combobox is an input with a popup that lets you select a value from a collection.

Loading...

Features

  • Supports selecting multiple values
  • Supports disabled options
  • Supports custom user input values
  • Supports mouse, touch, and keyboard interactions
  • Supports opening the combobox listbox with arrow keys, including automatically focusing the first or last item accordingly

Installation

Install the combobox package:

npm install @zag-js/combobox @zag-js/vue # or yarn add @zag-js/combobox @zag-js/vue

Anatomy

Check the combobox anatomy and part names.

Each part includes a data-part attribute to help identify them in the DOM.

Usage

Import the combobox package:

import * as combobox from "@zag-js/combobox"

These are the key exports:

  • machine - Behavior logic.
  • connect - Maps behavior to JSX props and event handlers.
  • collection - Creates a collection interface from an array of items.

Then use the framework integration helpers:

<script setup> import * as combobox from "@zag-js/combobox" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, ref } from "vue" const comboboxData = [ { label: "Zambia", code: "ZA" }, { label: "Benin", code: "BN" }, //... ] const options = ref(comboboxData) const collectionRef = computed(() => combobox.collection({ items: options.value, itemToValue: (item) => item.code, itemToString: (item) => item.label, }), ) const service = useMachine(combobox.machine, { id: "1", get collection() { return collectionRef.value }, onOpenChange() { options.value = comboboxData }, onInputValueChange({ inputValue }) { const filtered = comboboxData.filter((item) => item.label.toLowerCase().includes(inputValue.toLowerCase()), ) options.value = filtered.length > 0 ? filtered : comboboxData }, }) const api = computed(() => combobox.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <label v-bind="api.getLabelProps()">Select country</label> <div v-bind="api.getControlProps()"> <input v-bind="api.getInputProps()" /> <button v-bind="api.getTriggerProps()"></button> </div> </div> <div v-bind="api.getPositionerProps()"> <ul v-if="options.length > 0" v-bind="api.getContentProps()"> <li v-for="item in options" :key="item.code" v-bind="api.getItemProps({ item })" > {{ item.label }} </li> </ul> </div> </template>

Setting the initial value

Set defaultValue to define the initial combobox value.

const collection = combobox.collection({ items: [ { label: "Nigeria", value: "ng" }, { label: "Ghana", value: "gh" }, { label: "Kenya", value: "ke" }, //... ], }) const service = useMachine(combobox.machine, { id: useId(), collection, defaultValue: ["ng"], })

Controlled combobox

Use value and onValueChange to control the value programmatically.

<script setup lang="ts"> import { ref } from "vue" const valueRef = ref(["ng"]) const service = useMachine(combobox.machine, { get value() { return valueRef.value }, onValueChange(details) { valueRef.value = details.value }, }) </script>

Setting the initial input value

Use defaultInputValue to prefill the input on first render.

const service = useMachine(combobox.machine, { id: useId(), collection, defaultInputValue: "Nig", })

Controlling the input value

Use inputValue and onInputValueChange when you want to filter options as you type.

const service = useMachine(combobox.machine, { id: useId(), collection, inputValue, onInputValueChange({ inputValue }) { setInputValue(inputValue) setOptions(filterItems(inputValue)) }, })

Selecting multiple values

Set multiple to true to allow selecting multiple values.

const service = useMachine(combobox.machine, { id: useId(), collection, multiple: true, })

Using a custom object format

By default, the combobox collection expects an array of items with label and value properties. To use a custom object format, pass the itemToString and itemToValue properties to the collection function.

  • itemToString — A function that returns the string representation of an item. Used to compare items when filtering.
  • itemToValue — A function that returns the unique value of an item.
  • itemToDisabled — A function that returns the disabled state of an item.
const collection = combobox.collection({ // custom object format items: [ { id: 1, fruit: "Banana", available: true, quantity: 10 }, { id: 2, fruit: "Apple", available: false, quantity: 5 }, { id: 3, fruit: "Orange", available: true, quantity: 3 }, //... ], // convert item to string itemToString(item) { return item.fruit }, // convert item to value itemToValue(item) { return item.id }, // convert item to disabled state itemToDisabled(item) { return !item.available || item.quantity === 0 }, }) // use the collection const service = useMachine(combobox.machine, { id: useId(), collection, })

Rendering the selected values outside the combobox

By default, selected values are shown in the input. For multiple selection, it is often better to render selected values outside the combobox.

To do that:

  • Set the selectionBehavior to clear, which clears the input value when an item is selected.
  • Set the multiple property to true to allow selecting multiple values.
  • Render the selected values outside the combobox.
const service = useMachine(combobox.machine, { id: useId(), collection, selectionBehavior: "clear", multiple: true, })

Disabling the combobox

Set disabled to true to disable the combobox.

const service = useMachine(combobox.machine, { disabled: true, })

Disabling an option

Pass isItemDisabled to disable specific options.

const service = useMachine(combobox.machine, { id: useId(), collection: combobox.collection({ items: countries, isItemDisabled(item) { return item.disabled }, }), })

Close on select

By default, the menu closes when an option is selected with pointer or enter key. Set closeOnSelect to false to keep it open.

const service = useMachine(combobox.machine, { closeOnSelect: false, })

Controlling open state

Use open and onOpenChange for controlled popup state, or defaultOpen for an uncontrolled initial state.

const service = useMachine(combobox.machine, { id: useId(), collection, open, onOpenChange(details) { // details => { open: boolean; reason?: string; value: string[] } setOpen(details.open) }, })
const service = useMachine(combobox.machine, { id: useId(), collection, defaultOpen: true, })

Configuring popup trigger behavior

Use these props to fine-tune when the popup opens:

  • openOnClick to open when the input is clicked
  • openOnChange to control opening on input changes
  • openOnKeyPress to control opening on arrow key press
  • inputBehavior to set how typing and keyboard navigation affect highlight/input
const service = useMachine(combobox.machine, { id: useId(), collection, openOnClick: true, openOnChange: false, openOnKeyPress: false, inputBehavior: "autohighlight", })

Positioning the popup

Use positioning to control how the popup is placed.

const service = useMachine(combobox.machine, { id: useId(), collection, positioning: { placement: "bottom-start" }, })

Submitting forms on Enter

Set alwaysSubmitOnEnter to true if you want Enter to submit the form even while the popup is open.

const service = useMachine(combobox.machine, { id: useId(), collection, alwaysSubmitOnEnter: true, })

Making the combobox readonly

Set readOnly to true to make the combobox read-only.

const service = useMachine(combobox.machine, { readOnly: true, })

Required and invalid state

Set required and invalid for form validation and UI state.

const service = useMachine(combobox.machine, { id: useId(), collection, required: true, invalid: false, })

Listening for highlight changes

Use onHighlightChange to listen for highlighted option changes.

const service = useMachine(combobox.machine, { id: useId(), onHighlightChange(details) { // details => { highlightedValue: string | null; highlightedItem: CollectionItem | null } console.log(details) }, })

Setting the initial highlighted option

Use defaultHighlightedValue to set which option is highlighted when the popup opens.

const service = useMachine(combobox.machine, { id: useId(), collection, defaultHighlightedValue: "ng", })

Listening for item selection

Use onSelect to react to each selected item.

const service = useMachine(combobox.machine, { id: useId(), collection, onSelect(details) { // details => { value: string[]; itemValue: string } console.log(details.itemValue) }, })

Listening for value changes

Use onValueChange to listen for selected value changes.

const service = useMachine(combobox.machine, { onValueChange(details) { // details => { value: string[]; items: CollectionItem[] } console.log(details) }, })

Usage within forms

The combobox works in forms when you:

  • add a name so the selected value is included in FormData.

Set name to enable form submission support.

const service = useMachine(combobox.machine, { name: "countries", })

If the input belongs to a different form element, also set form.

const service = useMachine(combobox.machine, { id: useId(), collection, name: "countries", form: "checkout-form", })

Allowing custom values

By default, the combobox only allows values from the collection. Set allowCustomValue to true to allow custom values.

const service = useMachine(combobox.machine, { allowCustomValue: true, })

Customizing accessibility labels

Use translations to customize the trigger and clear button labels.

const service = useMachine(combobox.machine, { id: useId(), collection, translations: { triggerLabel: "Open countries", clearTriggerLabel: "Clear selection", }, })

Styling guide

Each combobox part includes a data-part attribute you can target in CSS.

Open and closed state

When the combobox opens or closes, data-state is added to content, control, input, and trigger parts.

[data-part="control"][data-state="open|closed"] { /* styles for open or closed control state */ } [data-part="input"][data-state="open|closed"] { /* styles for open or closed input state */ } [data-part="trigger"][data-state="open|closed"] { /* styles for open or closed trigger state */ } [data-part="content"][data-state="open|closed"] { /* styles for open or closed content state */ }

Focused State

When the combobox is focused, the data-focus attribute is added to the control and label parts.

[data-part="control"][data-focus] { /* styles for control focus state */ } [data-part="label"][data-focus] { /* styles for label focus state */ }

Disabled State

When the combobox is disabled, the data-disabled attribute is added to the label, control, trigger and option parts.

[data-part="label"][data-disabled] { /* styles for label disabled state */ } [data-part="control"][data-disabled] { /* styles for control disabled state */ } [data-part="trigger"][data-disabled] { /* styles for trigger disabled state */ } [data-part="item"][data-disabled] { /* styles for item disabled state */ }

Invalid State

When the combobox is invalid, the data-invalid attribute is added to the root, label, control and input parts.

[data-part="root"][data-invalid] { /* styles for root invalid state */ } [data-part="label"][data-invalid] { /* styles for label invalid state */ } [data-part="control"][data-invalid] { /* styles for control invalid state */ } [data-part="input"][data-invalid] { /* styles for input invalid state */ }

Selected State

When a combobox item is selected, the data-state attribute is added to the item part.

[data-part="item"][data-state="checked|unchecked"] { /* styles for item selected state */ }

Highlighted State

When a combobox item is highlighted, the data-highlighted attribute is added to the item part.

[data-part="item"][data-highlighted] { /* styles for item highlighted state */ }

Methods and Properties

Machine Context

The combobox machine exposes the following context properties:

  • openbooleanThe controlled open state of the combobox
  • defaultOpenbooleanThe initial open state of the combobox when rendered. Use when you don't need to control the open state of the combobox.
  • idsPartial<{ 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; }>The ids of the elements in the combobox. Useful for composition.
  • inputValuestringThe controlled value of the combobox's input
  • defaultInputValuestringThe initial value of the combobox's input when rendered. Use when you don't need to control the value of the combobox's input.
  • namestringThe `name` attribute of the combobox's input. Useful for form submission
  • formstringThe associate form of the combobox.
  • disabledbooleanWhether the combobox is disabled
  • readOnlybooleanWhether the combobox is readonly. This puts the combobox in a "non-editable" mode but the user can still interact with it
  • invalidbooleanWhether the combobox is invalid
  • requiredbooleanWhether the combobox is required
  • placeholderstringThe placeholder text of the combobox's input
  • defaultHighlightedValuestringThe initial highlighted value of the combobox when rendered. Use when you don't need to control the highlighted value of the combobox.
  • highlightedValuestringThe controlled highlighted value of the combobox
  • valuestring[]The controlled value of the combobox's selected items
  • defaultValuestring[]The initial value of the combobox's selected items when rendered. Use when you don't need to control the value of the combobox's selected items.
  • inputBehavior"autohighlight" | "autocomplete" | "none"Defines the auto-completion behavior of the combobox. - `autohighlight`: The first focused item is highlighted as the user types - `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated
  • selectionBehavior"clear" | "replace" | "preserve"The behavior of the combobox input when an item is selected - `replace`: The selected item string is set as the input value - `clear`: The input value is cleared - `preserve`: The input value is preserved
  • autoFocusbooleanWhether to autofocus the input on mount
  • openOnClickbooleanWhether to open the combobox popup on initial click on the input
  • openOnChangeboolean | ((details: InputValueChangeDetails) => boolean)Whether to show the combobox when the input value changes
  • allowCustomValuebooleanWhether to allow typing custom values in the input
  • alwaysSubmitOnEnterbooleanWhether to always submit on Enter key press, even if popup is open. Useful for single-field autocomplete forms where Enter should submit the form.
  • loopFocusbooleanWhether to loop the keyboard navigation through the items
  • positioningPositioningOptionsThe positioning options to dynamically position the menu
  • onInputValueChange(details: InputValueChangeDetails) => voidFunction called when the input's value changes
  • onValueChange(details: ValueChangeDetails<T>) => voidFunction called when a new item is selected
  • onHighlightChange(details: HighlightChangeDetails<T>) => voidFunction called when an item is highlighted using the pointer or keyboard navigation.
  • onSelect(details: SelectionDetails) => voidFunction called when an item is selected
  • onOpenChange(details: OpenChangeDetails) => voidFunction called when the popup is opened
  • translationsIntlTranslationsSpecifies the localized strings that identifies the accessibility elements and their states
  • collectionListCollection<T>The collection of items
  • multiplebooleanWhether to allow multiple selection. **Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`. It is recommended to render the selected items in a separate container.
  • closeOnSelectbooleanWhether to close the combobox when an item is selected.
  • openOnKeyPressbooleanWhether to open the combobox on arrow key press
  • scrollToIndexFn(details: ScrollToIndexDetails) => voidFunction to scroll to a specific index
  • compositebooleanWhether the combobox is a composed with other composite widgets like tabs
  • disableLayerbooleanWhether to disable registering this a dismissable layer
  • navigate(details: NavigateDetails) => voidFunction to navigate to the selected item
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
  • onPointerDownOutside(event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component
  • onFocusOutside(event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component
  • onInteractOutside(event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component

Machine API

The combobox api exposes the following methods:

  • focusedbooleanWhether the combobox is focused
  • openbooleanWhether the combobox is open
  • inputValuestringThe value of the combobox input
  • highlightedValuestringThe value of the highlighted item
  • highlightedItemVThe highlighted item
  • setHighlightValue(value: string) => voidThe value of the combobox input
  • clearHighlightValueVoidFunctionFunction to clear the highlighted value
  • syncSelectedItemsVoidFunctionFunction to sync the selected items with the value. Useful when `value` is updated from async sources.
  • selectedItemsV[]The selected items
  • hasSelectedItemsbooleanWhether there's a selected item
  • valuestring[]The selected item keys
  • valueAsStringstringThe string representation of the selected items
  • selectValue(value: string) => voidFunction to select a value
  • setValue(value: string[]) => voidFunction to set the value of the combobox
  • clearValue(value?: string) => voidFunction to clear the value of the combobox
  • focusVoidFunctionFunction to focus on the combobox input
  • setInputValue(value: string, reason?: InputValueChangeReason) => voidFunction to set the input value of the combobox
  • getItemState(props: ItemProps) => ItemStateReturns the state of a combobox item
  • setOpen(open: boolean, reason?: OpenChangeReason) => voidFunction to open or close the combobox
  • collectionListCollection<V>Function to toggle the combobox
  • reposition(options?: Partial<PositioningOptions>) => voidFunction to set the positioning options
  • multiplebooleanWhether the combobox allows multiple selections
  • disabledbooleanWhether the combobox is disabled

Data Attributes

Root
data-scope
combobox
data-part
root
data-invalid
Present when invalid
data-readonly
Present when read-only
Label
data-scope
combobox
data-part
label
data-readonly
Present when read-only
data-disabled
Present when disabled
data-invalid
Present when invalid
data-required
Present when required
data-focus
Present when focused
Control
data-scope
combobox
data-part
control
data-state
"open" | "closed"
data-focus
Present when focused
data-disabled
Present when disabled
data-invalid
Present when invalid
Input
data-scope
combobox
data-part
input
data-invalid
Present when invalid
data-state
"open" | "closed"
Trigger
data-scope
combobox
data-part
trigger
data-state
"open" | "closed"
data-invalid
Present when invalid
data-readonly
Present when read-only
data-disabled
Present when disabled
Content
data-scope
combobox
data-part
content
data-state
"open" | "closed"
data-nested
listbox
data-has-nested
listbox
data-placement
The placement of the content
data-empty
Present when the content is empty
List
data-scope
combobox
data-part
list
data-empty
Present when the content is empty
ClearTrigger
data-scope
combobox
data-part
clear-trigger
data-invalid
Present when invalid
Item
data-scope
combobox
data-part
item
data-highlighted
Present when highlighted
data-state
"checked" | "unchecked"
data-disabled
Present when disabled
data-value
The value of the item
ItemText
data-scope
combobox
data-part
item-text
data-state
"checked" | "unchecked"
data-disabled
Present when disabled
data-highlighted
Present when highlighted
ItemIndicator
data-scope
combobox
data-part
item-indicator
data-state
"checked" | "unchecked"
ItemGroup
data-scope
combobox
data-part
item-group
data-empty
Present when the content is empty

CSS Variables

Arrow
--arrow-size
The size of the arrow
--arrow-size-half
Half the size of the arrow
--arrow-background
Use this variable to style the arrow background
--arrow-offset
The offset position of the arrow
Positioner
--reference-width
The width of the reference element
--reference-height
The height of the root
--available-width
The available width in viewport
--available-height
The available height in viewport
--x
The x position for transform
--y
The y position for transform
--z-index
The z-index value
--transform-origin
The transform origin for animations
Content
--layer-index
The index of the dismissable in the layer stack
--nested-layer-count
The number of nested comboboxs
Backdrop
--layer-index
The index of the dismissable in the layer stack

Accessibility

Adheres to the Combobox WAI-ARIA design pattern.

Keyboard Interactions

  • ArrowDown
    When the combobox is closed, opens the listbox and highlights to the first option. When the combobox is open, moves focus to the next option.
  • ArrowUp
    When the combobox is closed, opens the listbox and highlights to the last option. When the combobox is open, moves focus to the previous option.
  • Home
    When the combobox is open, moves focus to the first option.
  • End
    When the combobox is open, moves focus to the last option.
  • Escape
    Closes the listbox.
  • Enter
    Selects the highlighted option and closes the combobox.
  • Esc
    Closes the combobox
Edit this page on GitHub