Skip to content
Components

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

Props

  • defaultValue
  • value
  • open
  • defaultOpen
  • placeholder
  • emptyText
  • size
  • disabled
  • name
  • label
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.

  • sm

  • mddefault

  • lg

  • xl

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

NameTypeValuesDefaultDescription
defaultValuestring--The value picked on first render, when you don't control it.
valuestring--The picked value, when you control it. Pair it with onValueChange.
onValueChangefunction--Called with the value of the option the user picks.
openboolean--Whether the list is open, when you control it. Pair it with onOpenChange.
defaultOpenboolean-falseOpens the list on first render.
onOpenChangefunction--Called with true or false when the list opens or closes.
placeholderstring--What to type, shown while the field is empty: Search countries. Not a label.
emptyText requiredstring--What the list says when the text matches no option: No country found.
sizeenumsm | md | lg | xlmdField 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.
disabledboolean-falseCan't be focused or opened; the neutral disabled look.
namestring--Form name. The picked value is submitted under it.

Slots

NameRequiredDescription
itemsyesComboboxItem elements (value, the text, optional textValue and disabled), or ComboboxGroup elements with a heading around them. Keep them direct children.

Anatomy

PartDescription
rootCombobox: the field and its list.
groupThe 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.
groupIconTabler CaretDown at the end, 16px (20px in xl), secondary.
placeholderPlaceholder text while nothing is picked or typed.
contentThe list under the field, as wide as it: Select's raised surface. It scrolls past 288px.
itemAn option, Select's option look: the md control height, inner corners, the neutral hover overlay when highlighted.
indicatorTabler Check, 16px, at the end of the picked option.
groupLabelThe heading of a ComboboxGroup, Select's group heading.
emptyemptyText when nothing matches: 13px secondary text.

Accessibility

Role: combobox

KeysAction
TypeFilters the options to those that contain the text, and opens the list.
ArrowDown, ArrowUpOpens the list, or moves through the matches; disabled options are skipped.
EnterPicks the highlighted option and closes the list.
EscapeCloses the list and puts back the picked option's text.
TabCloses 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.json

This 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").