Skip to content
shadcn.io is not affiliated with official shadcn/ui

Shadcn Combobox for React and Tailwind

Displays a searchable list of options.

Installation

bunx --bun shadcn@latest add https://kit.dev/r/combobox.json

Anatomy

Combobox
├── ComboboxLabel (optional)
├── ComboboxInput
│   ├── ComboboxControl (built in)
│   ├── input
│   ├── ComboboxTrigger
│   └── ComboboxClear (optional)
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        └── ComboboxGroup
            ├── ComboboxGroupLabel
            └── ComboboxItem

ComboboxContent portals the list and includes the positioner. ComboboxItem includes the check indicator. useCombobox is the machine hook for ComboboxRootProvider; useComboboxContext / ComboboxContext is in-tree.

Use Field for the visible label. For free-form suggestions, use Autocomplete.

Usage

import { useListCollection } from "@ark-ui/react/collection";
import { useFilter } from "@ark-ui/react/locale";
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox";
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
  initialItems: [
    { label: "Apple", value: "apple" },
    { label: "Banana", value: "banana" },
  ],
  filter: contains,
});

<Combobox
  collection={collection}
  onInputValueChange={({ inputValue }) => filter(inputValue)}
>
  <ComboboxInput placeholder="Select an option" />
  <ComboboxContent>
    <ComboboxEmpty />
    <ComboboxList>
      {collection.items.map((item) => (
        <ComboboxItem item={item} key={item.value}>
          {item.label}
        </ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

shadcn.io defaults openOnClick, lazyMount, and unmountOnExit to true (Ark: openOnClick false, presence false). inputBehavior is "none". Pass a collection and call filter from onInputValueChange. Selected values are always a string[].

Controlled

Control the selected value with value and onValueChange. Selected values are always a string[].

Root Provider

Use useCombobox with ComboboxRootProvider when you need the API outside the tree. Pass machine options (collection, multiple, inputBehavior, openOnClick, …) to useCombobox(), not to the provider.

States

Invalid

Disabled

Sizes

Size is set on ComboboxInput. sm is h-7, md is h-8, lg is h-9.

Small

Medium

Large

Examples

Auto highlight

Highlight the first matching item as the user types with inputBehavior="autohighlight".

Inline autocomplete

Set inputBehavior="autocomplete" so arrow keys complete the input with the highlighted item. Pair it with a startsWith filter for best results.

Group

Group related items with groupBy on the collection and collection.group() when rendering. ComboboxGroup accepts a heading for the label.

With Field

Field wires the label, helper text, and error text to the control.

Context

Read selected state with ComboboxContext or useComboboxContext.

Render items as links with asChild. Use selectionBehavior="preserve" so choosing a link does not replace the input value.

Rehydrate

When defaultValue or value is set before the collection loads, call syncSelectedItems() once the items are available so the input shows the selected label.

Highlight text

Highlight the matching query in each item with Highlight.

Dynamic

Build the collection from the current input. Useful for email-style suggestions.

Creatable

Let users add a value that is not in the list. Set allowCustomValue and upsert a temporary “create” item while typing, then replace it on select.

Multiple selection

Set multiple to select more than one item. Selection behavior becomes clear, so render selected items outside the input.

Load options from an async source with useAsyncList. Filter only on reason === "input-change" so highlighting and selection do not refetch.

Custom object

Map custom objects with itemToString and itemToValue on useListCollection.

Limit results

Pass limit to useListCollection to cap how many items are rendered.

Scrollable

Cap the list height with className on ComboboxContent. The positioner also exposes --available-height if you want to fit the viewport.

With clear button

Pass showClear to ComboboxInput. The clear control is shown when the input is not empty.

With start icon

Put an InputGroupAddon as a child of ComboboxInput. Decorative icons should be aria-hidden="true".

Guides

Combobox vs Autocomplete

ComboboxAutocomplete
Custom valuesClosed list unless you opt in with allowCustomValueAllowed (allowCustomValue)
Input behaviornoneautocomplete
TriggerShown (showTrigger)Hidden (showTrigger={false})
Use whenPick from a known set, with searchFree text with suggestions

Use Select when there is no search. Use Command for a command palette. Use Tags Input when the value is a list of removable tags.

Collections

Items live in an Ark collection, not a loose items prop on the root.

const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
  initialItems,
  filter: contains,
});

<Combobox
  collection={collection}
  onInputValueChange={({ inputValue }) => filter(inputValue)}
>

Call filter(inputValue) from onInputValueChange. Render collection.items (or collection.group() when grouping).

Custom objects

By default the collection expects { label, value }. Map other shapes with itemToString and itemToValue:

const { collection } = useListCollection({
  initialItems: [
    { country: "Canada", code: "CA", flag: "🇨🇦" },
  ],
  itemToString: (item) => item.country,
  itemToValue: (item) => item.code,
});

Type safety

Combobox is typed as Ark’s RootComponent, so onValueChange infers item types from the collection:

<Combobox
  collection={collection}
  onValueChange={(e) => {
    // e.items is T[]
    console.log(e.items);
  }}
>

Large datasets

Prefer limit on useListCollection so only a slice is in the DOM:

const { collection } = useListCollection({
  initialItems: items,
  limit: 10,
});

For very large lists, pass scrollToIndexFn and virtualize the list (for example with TanStack Virtual). Keyboard navigation needs that scroll helper.

Set navigate on the root when items are links, so in-app routing runs instead of a full navigation:

<Combobox
  collection={collection}
  navigate={(details) => {
    router.push(details.node.href);
  }}
>

NavigateDetails is { value: string; node: HTMLAnchorElement; href: string }.

Available size

The positioner (built into ComboboxContent) exposes CSS variables you can use on the list:

CSS variableDescription
--reference-widthWidth of the input control
--reference-heightHeight of the root
--available-widthAvailable width in the viewport
--available-heightAvailable height in the viewport

Example: match the input width and cap height to the viewport.

<ComboboxContent className="w-(--reference-width) max-h-[min(24rem,calc(var(--available-height)-100px))]" />

Multiple selection

When multiple is true, selectionBehavior is set to clear. Render selectedItems from context above the input (chips, tags, or a summary). Do not rely on the input to show every selected value.

API Reference

shadcn.io wraps Ark UI Combobox. Defaults below are shadcn.io Combobox values. They differ from Ark for openOnClick (true here, false in Ark) and lazyMount / unmountOnExit (true here, false in Ark).

asChild merges props onto a single child element.

Combobox

Root. Renders a div. Requires a collection.

PropTypeDefaultDescription
collectionListCollection<T>requiredItems to show. Create with useListCollection or createListCollection.
allowCustomValuebooleanfalseAllow values that are not in the collection.
alwaysSubmitOnEnterbooleanfalseSubmit on Enter even if the list is open. Useful for single-field forms.
asChildbooleanfalseRender the child element instead of a div.
autoFocusboolean-Focus the input on mount.
classNamestring-Class names on the root.
closeOnSelectboolean-Close the list when an item is selected.
compositebooleantrueTreat as composed with other composite widgets such as tabs.
defaultHighlightedValuestring | null-Uncontrolled initial highlighted value.
defaultInputValuestring""Uncontrolled initial input text.
defaultOpenboolean-Uncontrolled initial open state.
defaultValuestring[][]Uncontrolled selected values.
disabledboolean-Disable the combobox.
disableLayerboolean-Do not register as a dismissable layer.
formstring-Associated form id.
hideMode"display-none" | "activity""display-none"How to hide mounted-but-closed content. activity needs React 19+.
highlightedValuestring | null-Controlled highlighted value.
idstring-Unique id for the machine.
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 }>-Element ids for composition.
immediateboolean-Apply presence changes immediately instead of the next frame.
inputBehavior"none" | "autocomplete" | "autohighlight""none"none leaves the input as typed. autocomplete fills the input from the highlighted item. autohighlight highlights the first match while typing.
inputValuestring-Controlled input text.
invalidboolean-Mark as invalid.
lazyMountbooleantrueMount the list on first open.
loopFocusbooleantrueLoop keyboard focus through items.
multipleboolean-Allow more than one selected value. Forces selectionBehavior to clear.
namestring-name on the input for form submission.
navigate(details: NavigateDetails) => void-Called when a link item is chosen. { value, node, href }.
onExitComplete() => void-Called when the close animation finishes.
onFocusOutside(event: FocusOutsideEvent) => void-Called when focus moves outside.
onHighlightChange(details: HighlightChangeDetails<T>) => void-Called when the highlighted item changes. { highlightedValue, highlightedItem }.
onInputValueChange(details: InputValueChangeDetails) => void-Called when the input text changes. { inputValue, reason }. Filter the collection here.
onInteractOutside(event: InteractOutsideEvent) => void-Called on outside interaction.
onOpenChange(details: OpenChangeDetails) => void-Called when open state changes. { open, reason, value }.
onPointerDownOutside(event: PointerDownOutsideEvent) => void-Called on pointer down outside.
onSelect(details: SelectionDetails) => void-Called when an item is selected. { value, itemValue }.
onValueChange(details: ValueChangeDetails<T>) => void-Called when the selection changes. { value: string[]; items: T[] }.
openboolean-Controlled open state.
openOnChangeboolean | ((details: InputValueChangeDetails) => boolean)trueOpen the list when the input value changes.
openOnClickbooleantrueOpen the list on click in the input.
openOnKeyPressbooleantrueOpen the list on arrow keys.
placeholderstring-Placeholder on the root. Prefer placeholder on ComboboxInput.
positioningPositioningOptions{ placement: "bottom-start" }Floating position of the list.
presentboolean-Controlled presence.
readOnlyboolean-Non-editable, but still interactive.
requiredboolean-Mark as required.
scrollToIndexFn(details: ScrollToIndexDetails) => void-Scroll a virtualized list to an index. { index, immediate, getElement }.
selectionBehavior"clear" | "replace" | "preserve""replace"After select: clear the input, replace it with the item, or leave it. multiple forces clear.
skipAnimationOnMountbooleanfalseSkip the initial presence animation.
translationsIntlTranslations-{ triggerLabel?, clearTriggerLabel? } for assistive labels.
unmountOnExitbooleantrueUnmount the list after the close animation.
valuestring[]-Controlled selected values.
AttributeDescription
data-slotcombobox
data-scopecombobox
data-partroot
data-invalidPresent when invalid
data-readonlyPresent when read-only

ComboboxInput

Composed control: Input Group wrapping the searchable input, optional trigger, and optional clear button. Children render as start addons (icons, text). Renders inside ComboboxControl.

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Control height. sm is h-7, md is h-8, lg is h-9.
showTriggerbooleantrueShow the chevron that opens the list. Hidden while the clear button is visible.
showClearbooleanfalseShow a clear button when inputValue is not empty.
placeholderstring-Placeholder on the input.
disabledboolean-Disable the input.
classNamestring-Class names on the input group.
asChildbooleanfalseMerge onto the underlying input.

Native input attributes (type, aria-label, autoComplete, …) pass through to the input.

AttributeDescription
data-slotcombobox-input on the input
data-scopecombobox
data-partinput
data-invalidPresent when invalid
data-state"open" or "closed"
data-autofocusPresent when autoFocus is set

ComboboxControl

Optional wrapper for custom layouts. ComboboxInput already includes it.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the control.
AttributeDescription
data-slotcombobox-control
data-scopecombobox
data-partcontrol
data-state"open" or "closed"
data-focusPresent when focused
data-disabledPresent when disabled
data-invalidPresent when invalid

ComboboxTrigger

Opens the list. Used by ComboboxInput when showTrigger is true. Always asChild; the default child is an icon Button with aria-label="Toggle options".

PropTypeDefaultDescription
asChildbooleantrueAlways merges onto a single child.
classNamestring-Class names on the trigger.
focusableboolean-Whether the trigger is in the tab order.
AttributeDescription
data-slotcombobox-trigger
data-scopecombobox
data-parttrigger
data-state"open" or "closed"
data-invalidPresent when invalid
data-readonlyPresent when read-only
data-disabledPresent when disabled
data-focusablePresent when focusable

ComboboxClear

Clears the input. Used by ComboboxInput when showClear is true. Renders a button. The composed clear control uses aria-label="Clear".

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the clear control.
AttributeDescription
data-slotcombobox-clear
data-scopecombobox
data-partclear-trigger
data-invalidPresent when invalid

ComboboxFieldInput

Bare Ark input for custom layouts (for example composing with Tags Input). Prefer ComboboxInput for the standard control.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the input.

Native input attributes pass through.

AttributeDescription
data-slotcombobox-field-input
data-scopecombobox
data-partinput

ComboboxLabel

Optional visible label. Prefer Field (FieldLabel) so helper and error text stay associated. Renders a label.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the label.
AttributeDescription
data-slotcombobox-label
data-scopecombobox
data-partlabel
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only
data-requiredPresent when required

ComboboxContent

Portaled list surface. Includes the positioner. Renders a div.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the content.
AttributeDescription
data-slotcombobox-content
data-scopecombobox
data-partcontent
data-state"open" or "closed"
data-placementPlacement of the content
data-sideSide of the trigger the content is on
data-emptyPresent when there are no items
data-nestedPresent when nested in another listbox
data-has-nestedPresent when this list has nested listboxes
CSS variableDescription
--layer-indexIndex in the dismissable layer stack
--nested-layer-countNumber of nested comboboxes
--transform-originTransform origin for open/close animation
--reference-widthWidth of the input control (on the positioner)
--reference-heightHeight of the root (on the positioner)
--available-widthAvailable width in the viewport (on the positioner)
--available-heightAvailable height in the viewport (on the positioner)

ComboboxList

Wraps the options. Renders a div.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the list.
AttributeDescription
data-slotcombobox-list
data-scopecombobox
data-partlist
data-emptyPresent when there are no items

ComboboxItem

A single option. Always uses persistFocus so pointer leave does not clear highlight. Includes a check indicator unless showIndicator={false}. Renders a div.

PropTypeDefaultDescription
itemTrequiredCollection item.
showIndicatorbooleantrueShow the selected check.
asChildbooleanfalseMerge onto a single child (for example an <a>).
classNamestring-Class names on the item.
persistFocusbooleantrueKeep highlight when the pointer leaves.
AttributeDescription
data-slotcombobox-item
data-scopecombobox
data-partitem
data-highlightedPresent when highlighted
data-state"checked" or "unchecked"
data-disabledPresent when disabled
data-valueThe item value

The indicator is not a separate export.

AttributeDescription
data-slotcombobox-item-indicator
data-scopecombobox
data-partitem-indicator
data-state"checked" or "unchecked"

ComboboxGroup

Groups related options. Pass heading to render ComboboxGroupLabel.

PropTypeDefaultDescription
headingstring | ReactNode-Group label.
idstring-Group id.
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the group.
AttributeDescription
data-slotcombobox-group
data-scopecombobox
data-partitem-group
data-emptyPresent when the group is empty

ComboboxGroupLabel

Label for a group. Usually created by heading on ComboboxGroup. Renders a div.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the label.
AttributeDescription
data-slotcombobox-group-label
data-scopecombobox
data-partitem-group-label

ComboboxEmpty

Shown when the filtered collection has no items. Default children: No results found.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the empty state.
childrenReactNodeNo results found.Empty message.
AttributeDescription
data-slotcombobox-empty
data-scopecombobox
data-partempty

ComboboxRootProvider

Root alternative that takes the API from useCombobox. Renders a div.

PropTypeDefaultDescription
valueUseComboboxReturn<T>requiredReturn value of useCombobox().
asChildbooleanfalseRender the child element instead of a div.
hideMode"display-none" | "activity""display-none"How to hide mounted-but-closed content.
immediateboolean-Apply presence changes immediately.
lazyMountbooleantrueMount the list on first open.
onExitComplete() => void-Called when the close animation finishes.
presentboolean-Controlled presence.
skipAnimationOnMountbooleanfalseSkip the initial presence animation.
unmountOnExitbooleantrueUnmount after the close animation.

Pass collection, multiple, inputBehavior, openOnClick, and other machine options to useCombobox(), not to ComboboxRootProvider. To match Combobox, pass openOnClick: true.

AttributeDescription
data-slotcombobox
data-scopecombobox
data-partroot

useCombobox

Creates the combobox API for ComboboxRootProvider. Accepts the same options as Combobox except layout-only props.

const combobox = useCombobox({
  collection,
  onInputValueChange: (details) => filter(details.inputValue),
  openOnClick: true,
});

combobox.focus();

ComboboxContext / useComboboxContext

Render-prop or hook access to combobox state. Use inside Combobox or ComboboxRootProvider.

PropertyTypeDescription
focusedbooleanWhether the input is focused.
openbooleanWhether the list is open.
inputValuestringCurrent input text.
highlightedValuestring | nullValue of the highlighted item.
highlightedItemT | nullHighlighted item.
setHighlightValue(value: string) => voidHighlight an item by value.
clearHighlightValue() => voidClear the highlight.
syncSelectedItems() => voidSync selected items after an async collection load.
selectedItemsT[]Selected items.
hasSelectedItemsbooleanWhether any item is selected.
valuestring[]Selected values.
valueAsStringstringSelected items as a string.
selectValue(value: string) => voidSelect a value.
setValue(value: string[]) => voidSet the selection.
clearValue(value?: string) => voidClear one value, or all if omitted.
focus() => voidFocus the input.
setInputValue(value: string, reason?: InputValueChangeReason) => voidSet the input text.
getItemState(props: { item: T; persistFocus?: boolean }) => ItemStateState for one item (value, disabled, selected, highlighted).
setOpen(open: boolean, reason?: OpenChangeReason) => voidOpen or close the list.
collectionListCollection<T>Current collection.
reposition(options?: Partial<PositioningOptions>) => voidUpdate list position.
multiplebooleanWhether multiple selection is on.
disabledbooleanWhether the combobox is disabled.

ComboboxContext children: (context) => ReactNode.

ComboboxItemContext / useComboboxItemContext

Render-prop or hook access to one item. Use inside ComboboxItem.

PropertyTypeDescription
valuestringItem value.
disabledbooleanWhether the item is disabled.
selectedbooleanWhether the item is selected.
highlightedbooleanWhether the item is highlighted.

ComboboxItemContext children: (context) => ReactNode.

Accessibility

Complies with the Combobox WAI-ARIA design pattern. Label the input with Field (FieldLabel) or aria-label on ComboboxInput. Keep ComboboxEmpty so a filtered list is not a blank popup.

Keyboard support

KeyDescription
ArrowDownWhen closed, opens the list and highlights the first option. When open, moves to the next option.
ArrowUpWhen closed, opens the list and highlights the last option. When open, moves to the previous option.
HomeWhen open, moves to the first option.
EndWhen open, moves to the last option.
EnterSelects the highlighted option and closes the list.
EscapeCloses the list.