Combobox
Picks one value from a long list by typing to filter it: a country, a person, a repository. It has Input's box with a caret, and opens Select's list under the field; the arrow keys move through the matches and Enter picks one.
Preview
import { Combobox, ComboboxItem } from "@/components/ui/combobox"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
<Field className="w-full max-w-sm">
<FieldLabel>Fruit</FieldLabel>
<FieldControl>
<Combobox placeholder="Search fruits" emptyText="No fruit found.">
<ComboboxItem value="apricot">Apricot</ComboboxItem>
<ComboboxItem value="banana">Banana</ComboboxItem>
<ComboboxItem value="cherry">Cherry</ComboboxItem>
<ComboboxItem value="grape">Grape</ComboboxItem>
<ComboboxItem value="lemon">Lemon</ComboboxItem>
<ComboboxItem value="mango">Mango</ComboboxItem>
<ComboboxItem value="peach">Peach</ComboboxItem>
<ComboboxItem value="pear">Pear</ComboboxItem>
</Combobox>
</FieldControl>
</Field>Usage
When to use
- Pick one value from a list too long to scan, by typing part of it: a country, a person, a repository.
- A list whose options the user already knows by name.
When not to use
- A short list of options. Use Select, or RadioGroup to show them all.
- Free text. Use Input.
- Several values. Use Tokenizer, or Checkbox for a short list.
Variants
size
Field height from size-element-*. Match the Input or Button next to it: md in forms, sm only in dense mouse-first UI, lg or xl for the main field of a screen and on touch-first screens.
import { Combobox, ComboboxItem } from "@/components/ui/combobox"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
<Field className="w-full max-w-sm">
<FieldLabel>Fruit</FieldLabel>
<FieldControl>
<Combobox placeholder="Search fruits" emptyText="No fruit found." size="sm">
<ComboboxItem value="apricot">Apricot</ComboboxItem>
<ComboboxItem value="banana">Banana</ComboboxItem>
<ComboboxItem value="cherry">Cherry</ComboboxItem>
<ComboboxItem value="grape">Grape</ComboboxItem>
<ComboboxItem value="lemon">Lemon</ComboboxItem>
<ComboboxItem value="mango">Mango</ComboboxItem>
<ComboboxItem value="peach">Peach</ComboboxItem>
<ComboboxItem value="pear">Pear</ComboboxItem>
</Combobox>
</FieldControl>
</Field>
<Field className="w-full max-w-sm">
<FieldLabel>Fruit</FieldLabel>
<FieldControl>
<Combobox placeholder="Search fruits" emptyText="No fruit found.">
<ComboboxItem value="apricot">Apricot</ComboboxItem>
<ComboboxItem value="banana">Banana</ComboboxItem>
<ComboboxItem value="cherry">Cherry</ComboboxItem>
<ComboboxItem value="grape">Grape</ComboboxItem>
<ComboboxItem value="lemon">Lemon</ComboboxItem>
<ComboboxItem value="mango">Mango</ComboboxItem>
<ComboboxItem value="peach">Peach</ComboboxItem>
<ComboboxItem value="pear">Pear</ComboboxItem>
</Combobox>
</FieldControl>
</Field>
<Field className="w-full max-w-sm">
<FieldLabel>Fruit</FieldLabel>
<FieldControl>
<Combobox placeholder="Search fruits" emptyText="No fruit found." size="lg">
<ComboboxItem value="apricot">Apricot</ComboboxItem>
<ComboboxItem value="banana">Banana</ComboboxItem>
<ComboboxItem value="cherry">Cherry</ComboboxItem>
<ComboboxItem value="grape">Grape</ComboboxItem>
<ComboboxItem value="lemon">Lemon</ComboboxItem>
<ComboboxItem value="mango">Mango</ComboboxItem>
<ComboboxItem value="peach">Peach</ComboboxItem>
<ComboboxItem value="pear">Pear</ComboboxItem>
</Combobox>
</FieldControl>
</Field>
<Field className="w-full max-w-sm">
<FieldLabel>Fruit</FieldLabel>
<FieldControl>
<Combobox placeholder="Search fruits" emptyText="No fruit found." size="xl">
<ComboboxItem value="apricot">Apricot</ComboboxItem>
<ComboboxItem value="banana">Banana</ComboboxItem>
<ComboboxItem value="cherry">Cherry</ComboboxItem>
<ComboboxItem value="grape">Grape</ComboboxItem>
<ComboboxItem value="lemon">Lemon</ComboboxItem>
<ComboboxItem value="mango">Mango</ComboboxItem>
<ComboboxItem value="peach">Peach</ComboboxItem>
<ComboboxItem value="pear">Pear</ComboboxItem>
</Combobox>
</FieldControl>
</Field>Examples
Country
import { Combobox, ComboboxItem } from "@/components/ui/combobox"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
<Field className="w-full max-w-sm">
<FieldLabel>Country</FieldLabel>
<FieldControl>
<Combobox placeholder="Search countries" emptyText="No country found.">
<ComboboxItem value="de">Germany</ComboboxItem>
<ComboboxItem value="jp">Japan</ComboboxItem>
<ComboboxItem value="vn">Vietnam</ComboboxItem>
<ComboboxItem value="us">United States</ComboboxItem>
</Combobox>
</FieldControl>
</Field>Groups
A heading over each group; a disabled option stays in the list but can't be picked.
import { Combobox, ComboboxGroup, ComboboxItem } from "@/components/ui/combobox"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
<Field className="w-full max-w-sm">
<FieldLabel>Assignee</FieldLabel>
<FieldControl>
<Combobox defaultValue="ada" emptyText="No one found.">
<ComboboxGroup heading="Design">
<ComboboxItem value="ada">Ada Lovelace</ComboboxItem>
<ComboboxItem value="grace">Grace Hopper</ComboboxItem>
</ComboboxGroup>
<ComboboxGroup heading="Engineering">
<ComboboxItem value="alan">Alan Turing</ComboboxItem>
<ComboboxItem value="edsger" disabled>Edsger Dijkstra</ComboboxItem>
</ComboboxGroup>
</Combobox>
</FieldControl>
</Field>Do and don't
Do
- Use it for lists too long to scan, where users know what they look for.
- Write emptyText that says what was not found: No country found.
- Put it in a Field with a visible label.
Don't
- Don't use it for a short list; use Select or RadioGroup.
- Don't use it for free text; use Input.
- Don't use it for several values; use Tokenizer or Checkbox.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| defaultValue | string | - | - | The value picked on first render, when you don't control it. |
| value | string | - | - | The picked value, when you control it. Pair it with onValueChange. |
| onValueChange | function | - | - | Called with the value of the option the user picks. |
| open | boolean | - | - | Whether the list is open, when you control it. Pair it with onOpenChange. |
| defaultOpen | boolean | - | false | Opens the list on first render. |
| onOpenChange | function | - | - | Called with true or false when the list opens or closes. |
| placeholder | string | - | - | What to type, shown while the field is empty: Search countries. Not a label. |
| emptyText required | string | - | - | What the list says when the text matches no option: No country found. |
| size | enum | sm | md | lg | xl | md | Field height from size-element-*. Match the Input or Button next to it: md in forms, sm only in dense mouse-first UI, lg or xl for the main field of a screen and on touch-first screens. |
| disabled | boolean | - | false | Can't be focused or opened; the neutral disabled look. |
| name | string | - | - | Form name. The picked value is submitted under it. |
Slots
| Name | Required | Description |
|---|---|---|
| items | yes | ComboboxItem elements (value, the text, optional textValue and disabled), or ComboboxGroup elements with a heading around them. Keep them direct children. |
Anatomy
| Part | Description |
|---|---|
| root | Combobox: the field and its list. |
| group | The field box: Input's sunken fill, control border, corners and sizes, with the text field and the caret. It shows the field's focus ring, disabled look and danger border. |
| groupIcon | Tabler CaretDown at the end, 16px (20px in xl), secondary. |
| placeholder | Placeholder text while nothing is picked or typed. |
| content | The list under the field, as wide as it: Select's raised surface. It scrolls past 288px. |
| item | An option, Select's option look: the md control height, inner corners, the neutral hover overlay when highlighted. |
| indicator | Tabler Check, 16px, at the end of the picked option. |
| groupLabel | The heading of a ComboboxGroup, Select's group heading. |
| empty | emptyText when nothing matches: 13px secondary text. |
Accessibility
Role: combobox
| Keys | Action |
|---|---|
| Type | Filters the options to those that contain the text, and opens the list. |
| ArrowDown, ArrowUp | Opens the list, or moves through the matches; disabled options are skipped. |
| Enter | Picks the highlighted option and closes the list. |
| Escape | Closes the list and puts back the picked option's text. |
| Tab | Closes the list and moves on. |
- The field is a combobox with aria-expanded, aria-controls and aria-autocomplete="list"; the highlighted option is its aria-activedescendant, so focus stays in the field.
- Name it with a Field label or aria-label. The options are read with their selected state; a group is named by its heading.
- When no option matches, the list says emptyText.
- With name, the picked value is submitted under it in a hidden input.
Install
npx shadcn add https://werewolf-ui.vercel.app/r/combobox.jsonThis also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").