---
title: "Combobox"
description: "A text input with incremental filtering and single-option selection. Items are strings, and the empty and trigger labels are required."
kind: component
id: C-010
registry: "combobox"
docs: "https://koten.codehive.com.co/components/combobox"
markdown: "https://koten.codehive.com.co/md/components/combobox.md"
install: "npx shadcn@latest add https://koten.codehive.com.co/r/combobox.json"
language: en
---

# Combobox · 検索選択

> Component C-010 in the Koten UI catalog.

A text input with incremental filtering and single-option selection. Items are strings, and the empty and trigger labels are required.

> Japanese labels in this page are documentation metadata. Keep the consumer UI in the product's language and do not add Japanese decoration unless the user asks (R-031).

## Install

Install `koten-theme` first when the project does not have Koten tokens yet. Use the runner of your project.

```bash
# Node
npx shadcn@latest add https://koten.codehive.com.co/r/combobox.json

# Bun
bunx shadcn@latest add https://koten.codehive.com.co/r/combobox.json
```

## Files

- `@/components/ui/combobox.tsx`
- `@/lib/koten/cn.ts`
- `@/lib/koten/format.ts`
- `@/lib/koten/intl.ts`
- `@/lib/koten/labels.ts`
- `@/lib/koten/provider.tsx`

## Reference

- Upstream primitive: https://base-ui.com/react/components/combobox

## Examples

### Search and states

emptyLabel covers the no-match case and triggerLabel names the icon-only button.

```tsx
<Combobox
  label="Customer"
  items={customers}
  emptyLabel="No customers"
  triggerLabel="Open customers"
  placeholder="Search customer…"
/>
<Combobox
  label="Locked customer"
  items={customers}
  emptyLabel="No customers"
  triggerLabel="Open customers"
  disabled
/>
```

### Controlled selection

onValueChange returns the chosen string or null, with no intermediate objects.

```tsx
const [customer, setCustomer] = useState<string | null>("HIKARI Store");

<Combobox
  label="Customer"
  items={customers}
  value={customer ?? undefined}
  onValueChange={setCustomer}
  emptyLabel="No customers"
  triggerLabel="Open customers"
  placeholder="Search customer…"
/>
<p className="text-2xs text-muted">
  Selected: <span className="font-semibold text-ink">{customer ?? "none"}</span>
</p>
```

### Use inside a form

Combobox has no help or error props; both are composed below with the same tokens, and the error uses role=alert.

```tsx
<form
  className="grid w-full max-w-sm gap-[var(--koten-gap)]"
  onSubmit={(event) => event.preventDefault()}
>
  <div className="grid gap-[calc(var(--koten-gap)*0.5)]">
    <Combobox
      label="Customer"
      items={customers}
      emptyLabel="No customers"
      triggerLabel="Open customers"
      placeholder="Search customer…"
    />
    <p className="text-2xs leading-relaxed text-faint">
      Billing and delivery addresses follow the customer record.
    </p>
    <p role="alert" className="text-2xs leading-relaxed text-danger">
      Pick an existing customer before saving.
    </p>
  </div>
  <div className="flex justify-end">
    <Button type="submit" variant="primary">Save order</Button>
  </div>
</form>
```

## Props

| Prop | Type | Default | Notes |
| --- | --- | --- | --- |
| `label` | `string` | `—` | Visible label linked to the input through htmlFor/id; required. |
| `items` | `readonly string[]` | `—` | Strings; the input filters by match while typing. |
| `emptyLabel` | `string` | `—` | Visible message when no item matches. |
| `triggerLabel` | `string` | `—` | aria-label of the icon-only button that opens the list. |
| `placeholder` | `string` | `Buscar…` | Input text shown when it is empty. |
| `value / defaultValue` | `string` | `—` | Controlled or uncontrolled. |
| `onValueChange` | `(value: string \| null) => void` | `—` | Returns the chosen string, or null when the selection is cleared. |
| `disabled / name` | `boolean / string` | `false / —` | name submits the value with the form; disabled blocks the input and the trigger. |

## Accessibility

- The input is labelled through htmlFor/id, and the icon trigger carries triggerLabel as its aria-label.
- Filtering happens while typing; emptyLabel provides the visible message when nothing matches.
- Escape closes the popup and keeps the typed text so it can be corrected.

## Sources

- Catalog page: https://koten.codehive.com.co/components/combobox
- Markdown: https://koten.codehive.com.co/md/components/combobox.md
- Repository: https://github.com/codehive-colombia/koten
