# Combobox

- Page: <https://kznlabs.github.io/labsUI/components/combobox>

## Combobox (`combobox`, component)

A Select you can type into: the query filters by label and keywords (prefix matches first), arrows move, Enter picks. multiple keeps the list open and shows picks as removable tags. Submits through hidden inputs.

- Install: `npx @kznlabs/labsui add combobox`
- Docs: <https://kznlabs.github.io/labsUI/components/combobox>
- Brings: field, label, list, platform, tag-input, use-anchor, use-controllable-state, use-dismiss, utils
- npm: clsx@^2.1.1, lucide-react@1.63.0, motion@14.7.1, tailwind-merge@^3.7.0

### API

- `<Combobox>`: Select you can type into: the input filters the list, Up and Down move, Enter picks, Esc closes.
Single shows the pick in the input; `multiple` shows picks as tags and keeps the list open.
  - `options: ComboboxOption[]`: Choices; filtered as the user types.
  - `aria-describedby?: string`: Ids of hint or error text; Field fills it in.
  - `aria-invalid?: boolean`: Error styling; Field sets it from `error`.
  - `aria-label?: string`: Accessible name when there is no visible label.
  - `defaultValue?: string | string[]`: Value, or values when `multiple`, at first render (uncontrolled).
  - `disabled?: boolean`: Blocks typing and opening.
  - `emptyText?: ReactNode`: Shown in the list when nothing matches.
  - `id?: string`: Id of the input; Field fills it in.
  - `multiple?: boolean`: Picks stay open and show as removable tags; Backspace removes the last one.
  - `name?: string`: Submits the value(s) through hidden inputs (one per value when multiple).
  - `onValueChange?: ((value: string) => void) | ((value: string[]) => void)`: Called with the picked value, or every picked value when `multiple`.
  - `placeholder?: string`: Field text when nothing is picked.
  - `required?: boolean`: Form submit needs a value.
  - `value?: string | string[]`: Controlled value, or values when `multiple`; pair with `onValueChange`.

- `type ComboboxOption = { value: string; label: string; icon?: ReactNode; keywords?: string[]; disabled?: boolean; }`

### Usage

```tsx
<Combobox options={channels} value={channel} onValueChange={setChannel} placeholder="Pick a channel…" />
```

### Example: channel-picker.tsx

```tsx
"use client";

import { Hash } from "lucide-react";
import { useState } from "react";
import { Combobox } from "@/components/ui/combobox";
import { Field } from "@/components/ui/field";

export function ChannelPicker({ channels }: { channels: string[] }) {
  const [channel, setChannel] = useState("");
  return (
    <Field label="Post clips to">
      <Combobox
        name="channel"
        options={channels.map((c) => ({ value: c, label: c, icon: <Hash /> }))}
        value={channel}
        onValueChange={setChannel}
        placeholder="Pick a channel…"
      />
    </Field>
  );
}
```

### Example: role-picker.tsx

```tsx
"use client";

import { useState } from "react";
import { Combobox } from "@/components/ui/combobox";

const roles = [
  { value: "admin", label: "Admin", keywords: ["owner"] },
  { value: "moderator", label: "Moderator", keywords: ["mod"] },
  { value: "member", label: "Member" },
];

export function RolePicker() {
  const [picked, setPicked] = useState<string[]>([]);
  return (
    <Combobox
      multiple
      aria-label="Roles"
      options={roles}
      value={picked}
      onValueChange={setPicked}
      placeholder="Add roles…"
    />
  );
}
```
