Combobox

A select you type into: the input is the trigger, items filter by label or extra keywords across groups, arrow keys move a sliding highlight, and the list springs open in a portal that holds its side while closing. Value, open state and query are controllable.

Preview

Combobox preview width

Install

Add it with the shadcn CLI, or copy the source manually.

$ bunx --bun shadcn add @karaui/combobox
@karaui not found? Add it to components.json
{
  "registries": {
    "@karaui": "https://karaui.kamisama-v0.my.id/r/{name}.json"
  }
}

@karaui is in shadcn's public registry directory, so the latest CLI needs no setup. On an older CLI, merge this into the existing file once and every @karaui/… install resolves.

API Reference

ComboboxContent

side?
"bottom" | "top"
bottom
align?
"end" | "start" | "center"
start
sideOffset?
number
6
avoidCollisions?
boolean
true
className?
string
—

Combobox

value?
string
—
defaultValue?
string
—
onValueChange?
((value: string) => void)
—
open?
boolean
—
defaultOpen?
boolean
false
onOpenChange?
((open: boolean) => void)
—
query?
string
—
defaultQuery?
string
onQueryChange?
((query: string) => void)
—
filter?
ComboboxFilter
(value, query, keywords) => { const needle = query.trim().toLocaleLowerCase(); if (!needle) return true; const haystack = [value, ...keywords].join(" ").toLocaleLowerCase(); let queryIndex = 0; for (const character of haystack) { if (character === needle[queryIndex]) queryIndex += 1; if (queryIndex === needle.length) return true; } return false; }
disabled?
boolean
false
className?
string
—

ComboboxEmpty

className?
string
—

ComboboxGroup

className?
string
—

ComboboxItem

value
string
—
textValue?
string
—
keywords?
string[]
[]
disabled?
boolean
false
onSelect?
((value: string) => void)
—
className?
string
—

ComboboxLabel

className?
string
—

ComboboxList

ariaLabel?
string
Options
className?
string
—

ComboboxSeparator

className?
string
—

ComboboxInput

ref?
Ref<HTMLInputElement>
—
wrapperClassName?
string
—
className?
string
—

ComboboxTrigger

className?
string
—

ComboboxValue

placeholder?
ReactNode
Select an option
className?
string
—

Composition

The trigger wraps the text field. Items, groups, labels, separators and the empty message go in the content, which renders through a body portal.

Combobox
├── ComboboxTrigger
│   └── ComboboxInput
└── ComboboxContent
    └── ComboboxList
        ├── ComboboxEmpty
        ├── ComboboxGroup
        │   ├── ComboboxLabel
        │   └── ComboboxItem
        └── ComboboxSeparator

How it works

Use it when a plain select has too many options to scroll. The text field is the trigger, so people type to shrink the list and never leave the field. The component tracks the query, the highlighted row and the chosen value; your app supplies the options and whatever form wraps them.

Updated