Dice UI
Components

Combobox

An input with a popover that helps users filter through a list of options.

Installation

npm install @diceui/combobox

Installation with shadcn/ui

CLI

npx shadcn@latest add "https://diceui.com/r/combobox"

Manual

Install the following dependencies:

npm install @diceui/combobox

Copy and paste the following code into your project.

import * as ComboboxPrimitive from "@diceui/combobox";
import { Check, ChevronDown, X } from "lucide-react";
import * as React from "react";
 
import { cn } from "@/lib/utils";
 
const Combobox = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Root>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Root
    data-slot="combobox"
    ref={ref}
    className={cn(className)}
    {...props}
  />
)) as ComboboxPrimitive.ComboboxRootComponentProps;
Combobox.displayName = ComboboxPrimitive.Root.displayName;
 
const ComboboxLabel = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Label>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Label
    data-slot="combobox-label"
    ref={ref}
    className={cn("px-0.5 py-1.5 font-semibold text-sm", className)}
    {...props}
  />
));
ComboboxLabel.displayName = ComboboxPrimitive.Label.displayName;
 
const ComboboxAnchor = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Anchor>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Anchor>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Anchor
    data-slot="combobox-anchor"
    ref={ref}
    className={cn(
      "relative flex h-9 w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 shadow-xs data-focused:ring-1 data-focused:ring-ring",
      className,
    )}
    {...props}
  />
));
ComboboxAnchor.displayName = ComboboxPrimitive.Anchor.displayName;
 
const ComboboxInput = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Input>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Input>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Input
    data-slot="combobox-input"
    ref={ref}
    className={cn(
      "flex h-9 w-full rounded-md bg-transparent text-base placeholder:text-muted-foreground focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
      className,
    )}
    {...props}
  />
));
ComboboxInput.displayName = ComboboxPrimitive.Input.displayName;
 
const ComboboxTrigger = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
  <ComboboxPrimitive.Trigger
    data-slot="combobox-trigger"
    ref={ref}
    className={cn(
      "flex shrink-0 items-center justify-center rounded-r-md border-input bg-transparent text-muted-foreground transition-colors hover:text-foreground/80 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
      className,
    )}
    {...props}
  >
    {children || <ChevronDown className="h-4 w-4" />}
  </ComboboxPrimitive.Trigger>
));
ComboboxTrigger.displayName = ComboboxPrimitive.Trigger.displayName;
 
const ComboboxCancel = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Cancel>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Cancel>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Cancel
    data-slot="combobox-cancel"
    ref={ref}
    className={cn(
      "-translate-y-1/2 absolute top-1/2 right-1 flex h-6 w-6 items-center justify-center rounded-sm bg-background opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none",
      className,
    )}
    {...props}
  />
));
ComboboxCancel.displayName = ComboboxPrimitive.Cancel.displayName;
 
const ComboboxBadgeList = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.BadgeList>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.BadgeList>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.BadgeList
    data-slot="combobox-badge-list"
    ref={ref}
    className={cn("flex flex-wrap items-center gap-1.5", className)}
    {...props}
  />
));
ComboboxBadgeList.displayName = ComboboxPrimitive.BadgeList.displayName;
 
const ComboboxBadgeItem = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.BadgeItem>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.BadgeItem>
>(({ className, children, ...props }, ref) => (
  <ComboboxPrimitive.BadgeItem
    data-slot="combobox-badge-item"
    ref={ref}
    className={cn(
      "inline-flex items-center justify-between gap-1 rounded-sm bg-secondary px-2 py-0.5",
      className,
    )}
    {...props}
  >
    <span className="truncate text-[13px] text-secondary-foreground">
      {children}
    </span>
    <ComboboxPrimitive.BadgeItemDelete
      data-slot="combobox-badge-item-delete"
      className="shrink-0 rounded p-0.5 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-highlighted:bg-destructive"
    >
      <X className="h-3 w-3" />
    </ComboboxPrimitive.BadgeItemDelete>
  </ComboboxPrimitive.BadgeItem>
));
ComboboxBadgeItem.displayName = ComboboxPrimitive.BadgeItem.displayName;
 
const ComboboxContent = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Content>
>(({ sideOffset = 6, className, children, ...props }, ref) => (
  <ComboboxPrimitive.Portal>
    <ComboboxPrimitive.Content
      data-slot="combobox-content"
      ref={ref}
      sideOffset={sideOffset}
      className={cn(
        "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-fit min-w-[var(--dice-anchor-width)] origin-[var(--dice-transform-origin)] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in",
        className,
      )}
      {...props}
    >
      {children}
    </ComboboxPrimitive.Content>
  </ComboboxPrimitive.Portal>
));
ComboboxContent.displayName = ComboboxPrimitive.Content.displayName;
 
const ComboboxLoading = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Loading>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Loading>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Loading
    data-slot="combobox-loading"
    ref={ref}
    className={cn("py-6 text-center text-sm", className)}
    {...props}
  >
    Loading...
  </ComboboxPrimitive.Loading>
));
ComboboxLoading.displayName = ComboboxPrimitive.Loading.displayName;
 
const ComboboxEmpty = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Empty>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Empty>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Empty
    data-slot="combobox-empty"
    ref={ref}
    className={cn("py-6 text-center text-sm", className)}
    {...props}
  />
));
ComboboxEmpty.displayName = ComboboxPrimitive.Empty.displayName;
 
const ComboboxGroup = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Group>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Group>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Group
    data-slot="combobox-group"
    ref={ref}
    className={cn("overflow-hidden", className)}
    {...props}
  />
));
ComboboxGroup.displayName = ComboboxPrimitive.Group.displayName;
 
const ComboboxGroupLabel = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.GroupLabel>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.GroupLabel>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.GroupLabel
    data-slot="combobox-group-label"
    ref={ref}
    className={cn(
      "px-2 py-1.5 font-semibold text-muted-foreground text-xs",
      className,
    )}
    {...props}
  />
));
ComboboxGroupLabel.displayName = ComboboxPrimitive.GroupLabel.displayName;
 
const ComboboxItem = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Item> & {
    outset?: boolean;
  }
>(({ className, children, outset, ...props }, ref) => (
  <ComboboxPrimitive.Item
    data-slot="combobox-item"
    ref={ref}
    className={cn(
      "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 text-sm outline-hidden data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-50",
      outset ? "pr-8 pl-2" : "pr-2 pl-8",
      className,
    )}
    {...props}
  >
    <ComboboxPrimitive.ItemIndicator
      className={cn(
        "absolute flex h-3.5 w-3.5 items-center justify-center",
        outset ? "right-2" : "left-2",
      )}
    >
      <Check className="h-4 w-4" />
    </ComboboxPrimitive.ItemIndicator>
    <ComboboxPrimitive.ItemText>{children}</ComboboxPrimitive.ItemText>
  </ComboboxPrimitive.Item>
));
ComboboxItem.displayName = ComboboxPrimitive.Item.displayName;
 
const ComboboxSeparator = React.forwardRef<
  React.ComponentRef<typeof ComboboxPrimitive.Separator>,
  React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Separator>
>(({ className, ...props }, ref) => (
  <ComboboxPrimitive.Separator
    data-slot="combobox-separator"
    ref={ref}
    className={cn("-mx-1 my-1 h-px bg-muted", className)}
    {...props}
  />
));
ComboboxSeparator.displayName = ComboboxPrimitive.Separator.displayName;
 
export {
  Combobox,
  ComboboxAnchor,
  ComboboxInput,
  ComboboxTrigger,
  ComboboxCancel,
  ComboboxBadgeList,
  ComboboxBadgeItem,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxGroupLabel,
  ComboboxItem,
  ComboboxLabel,
  ComboboxLoading,
  ComboboxSeparator,
};

Layout

Import the parts, and compose them together.

import * as Combobox from "@diceui/combobox";
 
<Combobox.Root>
  <Combobox.Label />
  <Combobox.Anchor>
    <Combobox.BadgeList>
      <Combobox.BadgeItem>
        <Combobox.BadgeItemDelete />
      </Combobox.BadgeItem>
    </Combobox.BadgeList>
    <Combobox.Input />
    <Combobox.Trigger />
    <Combobox.Cancel />
  </Combobox.Anchor>
  <Combobox.Portal>
    <Combobox.Content>
      <Combobox.Arrow />
      <Combobox.Loading />
      <Combobox.Empty />
      <Combobox.Group>
        <Combobox.GroupLabel />
        <Combobox.Item>
          <Combobox.ItemText />
          <Combobox.ItemIndicator />
        </Combobox.Item>
      </Combobox.Group>
      <Combobox.Separator />
    </Combobox.Content>
  </Combobox.Portal>
</Combobox.Root>

Examples

With Groups

With Multiple Selection

With Custom Filter

With Debounce

With Virtualization

With Tags Input

API Reference

Root

The container for all combobox parts.

PropTypeDefault
defaultValue?
Value<Multiple>
-
value?
Value<Multiple>
-
onValueChange?
(value: Value<Multiple>) => void
-
asChild?
boolean
-
open?
boolean
-
defaultOpen?
boolean
false
onOpenChange?
(open: boolean) => void
-
inputValue?
string
-
onInputValueChange?
(value: string) => void
-
onFilter?
(options: string[], inputValue: string) => string[]
-
autoHighlight?
boolean
false
disabled?
boolean
-
exactMatch?
boolean
false
manualFiltering?
boolean
false
loop?
boolean
false
modal?
boolean
false
multiple?
Multiple
false
openOnFocus?
boolean
false
preserveInputOnBlur?
boolean
false
readOnly?
boolean
false
required?
boolean
false
name?
string
-
Data AttributeValue
[data-state]"open" | "closed"
[data-disabled]Present when disabled

Label

An accessible label that describes the combobox. Associates with the input element for screen readers.

PropTypeDefault
asChild?
boolean
-

Anchor

A wrapper element that positions the combobox popover relative to the input and trigger. Provides the reference point for popover positioning.

PropTypeDefault
asChild?
boolean
-
preventInputFocus?
boolean
false
Data AttributeValue
[data-state]"open" | "closed"
[data-anchor]Present when the anchor is present
[data-disabled]Present when disabled
[data-focused]Present when the anchor is focused

Trigger

A button that toggles the combobox popover. Handles focus management and keyboard interactions for opening/closing the popover.

PropTypeDefault
asChild?
boolean
-
Data AttributeValue
[data-state]"open" | "closed"
[data-disabled]Present when disabled

Input

The text input field that users can type into to filter options.

PropTypeDefault
asChild?
boolean
-

BadgeList

A container for displaying selected items as badges in a multi-select combobox.

PropTypeDefault
asChild?
boolean
-
forceMount?
boolean
false
orientation?
"horizontal" | "vertical"
"horizontal"
Data AttributeValue
[data-orientation]"horizontal" | "vertical"

BadgeItem

An individual badge representing a selected item in a multi-select combobox.

PropTypeDefault
asChild?
boolean
-
value
string
-
disabled?
boolean
false
Data AttributeValue
[data-disabled]Present when the badge is disabled
[data-highlighted]Present when the badge is highlighted
[data-orientation]"horizontal" | "vertical"

BadgeItemDelete

A button to remove a selected item from the multi-select combobox.

PropTypeDefault
asChild?
boolean
-
Data AttributeValue
[data-disabled]Present when the parent badge is disabled
[data-highlighted]Present when the parent badge is highlighted

Cancel

A button that clears the input value and resets the filter.

PropTypeDefault
asChild?
boolean
-
forceMount?
boolean
false
Data AttributeValue
[data-disabled]Present when disabled

Portal

A portal for rendering the combobox content outside of its DOM hierarchy.

PropTypeDefault
container?
Element | DocumentFragment
document.body

Content

The popover container for combobox items. Positions the combobox popover relative to the anchor.

PropTypeDefault
side?
Side
"bottom"
sideOffset?
number
4
align?
Align
"start"
alignOffset?
number
0
collisionBoundary?
Boundary
-
collisionPadding?
number | Partial<Record<Side, number>>
0
arrowPadding?
number
0
sticky?
"partial" | "always"
"partial"
strategy?
Strategy
"absolute"
avoidCollisions?
boolean
true
fitViewport?
boolean
false
forceMount?
boolean
false
hideWhenDetached?
boolean
false
trackAnchor?
boolean
true
asChild?
boolean
-
onEscapeKeyDown?
(event: KeyboardEvent) => void
-
onPointerDownOutside?
(event: PointerDownOutsideEvent) => void
-
Data AttributeValue
[data-state]"open" | "closed"
[data-side]"top" | "right" | "bottom" | "left"
[data-align]"start" | "center" | "end"
CSS VariableDescription
--dice-transform-originTransform origin for anchor positioning.
--dice-anchor-widthWidth of the anchor element.
--dice-anchor-heightHeight of the anchor element.
--dice-available-widthAvailable width in the viewport for the popover element.
--dice-available-heightAvailable height in the viewport for the popover element.

Arrow

A visual arrow element that points to the anchor.

PropTypeDefault
asChild?
boolean
-

Loading

A loading indicator for asynchronous filtering operations.

PropTypeDefault
label?
string
-
asChild?
boolean
-
value?
number
null
max?
number
100

Empty

A placeholder component displayed when no options match the current filter.

PropTypeDefault
asChild?
boolean
-
keepVisible?
boolean
false

Group

A container for logically grouping related options.

PropTypeDefault
asChild?
boolean
-
forceMount?
boolean
false

GroupLabel

A label that describes a group of options.

PropTypeDefault
asChild?
boolean
-

Item

An interactive item in the combobox list.

PropTypeDefault
label?
string
-
onSelect?
(value: string) => void
-
asChild?
boolean
-
value
string
-
disabled?
boolean
-
Data AttributeValue
[data-highlighted]Present when the item is highlighted
[data-disabled]Present when the item is disabled
[data-state]"checked" | "unchecked"

ItemText

The textual content of an item.

PropTypeDefault
asChild?
boolean
-

ItemIndicator

A visual indicator for selected options.

PropTypeDefault
asChild?
boolean
-
forceMount?
boolean
false

Separator

A visual divider for separating options or groups.

PropTypeDefault
asChild?
boolean
-
keepVisible?
boolean
false

Accessibility

Keyboard Interactions

KeyDescription
EnterWhen open, selects the highlighted option. When a badge is highlighted in multiple mode, removes the badge.
ArrowUpWhen open, highlights the previous option.
ArrowDownWhen open, highlights the next option.
ArrowLeftIn multiple mode: When cursor is at start of input, closes the menu and highlights the last badge. When a badge is highlighted, moves highlight to previous badge.
ArrowRightIn multiple mode: When a badge is highlighted, moves highlight to next badge. If on last badge, removes highlight and focuses input.
BackspaceDeleteIn multiple mode: When input is empty, removes the last badge. When a badge is highlighted, removes the highlighted badge.
HomeWhen open, highlights the first option.
EndWhen open, highlights the last option.
PageUpWhen open and modal is enabled, highlights the previous option.
PageDownWhen open and modal is enabled, highlights the next option.
EscapeCloses the combobox popover, returns focus to the input, and resets or restores the input value.