---
type: doc
title: Combobox
description: Autocomplete input with a list of suggestions.
sidebar:
  badge:
    text: ⋅
    variant: success
---

> Fulldev UI for Astro. The `@fulldev` registry in `components.json` points at `https://ui.full.dev/r/styles/{style}/{name}.json`; see [installation](/astro/docs/installation.md).

```astro title="src/components/examples/combobox/combobox-demo.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---

<Combobox>
  <ComboboxInput placeholder="Select a framework" />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {frameworks.map((item) => (
        <ComboboxItem value={item}>{item}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Installation

```bash
npx shadcn@latest add @fulldev/combobox
```

## Usage

```ts
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"
```

```astro
<Combobox>
  <ComboboxInput placeholder="Select a framework" />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      <ComboboxItem value="Next.js">Next.js</ComboboxItem>
      <ComboboxItem value="SvelteKit">SvelteKit</ComboboxItem>
      <ComboboxItem value="Astro">Astro</ComboboxItem>
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

Items are authored markup, so render a data array with `.map()`. Data Slot
filters items by their label and selects one value at a time.

## Composition

Place `ComboboxEmpty` inside `ComboboxList`, as required by Data Slot.

```text
Combobox
├── ComboboxInput
└── ComboboxContent
    └── ComboboxList
        ├── ComboboxEmpty
        ├── ComboboxItem
        └── ComboboxItem
```

Put `ComboboxSeparator` between groups as a direct child of `ComboboxList`:
Data Slot only shows separators there, and hides them when a filter leaves no
items on one side.

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

## Basic

A simple combobox with a list of frameworks.

```astro title="src/components/examples/combobox/combobox-basic.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---

<Combobox>
  <ComboboxInput placeholder="Select a framework" />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {frameworks.map((item) => (
        <ComboboxItem value={item}>{item}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Multiple

The Astro combobox has no multiple selection or chips, so the official Multiple and RTL examples, which both use chips, are in the [React Combobox](/react/components/combobox/) docs only.

## Clear Button

Use the `showClear` prop on `ComboboxInput` to show a clear button.

```astro title="src/components/examples/combobox/combobox-clear.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---

<Combobox defaultValue={frameworks[0]}>
  <ComboboxInput placeholder="Select a framework" showClear />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {frameworks.map((item) => (
        <ComboboxItem value={item}>{item}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Groups

Use `ComboboxGroup` and `ComboboxSeparator` to group items.

```astro title="src/components/examples/combobox/combobox-groups.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
  ComboboxSeparator,
} from "@/components/ui/combobox"

const timezones = [
  {
    value: "Americas",
    items: [
      "(GMT-5) New York",
      "(GMT-8) Los Angeles",
      "(GMT-6) Chicago",
      "(GMT-5) Toronto",
      "(GMT-8) Vancouver",
      "(GMT-3) São Paulo",
    ],
  },
  {
    value: "Europe",
    items: [
      "(GMT+0) London",
      "(GMT+1) Paris",
      "(GMT+1) Berlin",
      "(GMT+1) Rome",
      "(GMT+1) Madrid",
      "(GMT+1) Amsterdam",
    ],
  },
  {
    value: "Asia/Pacific",
    items: [
      "(GMT+9) Tokyo",
      "(GMT+8) Shanghai",
      "(GMT+8) Singapore",
      "(GMT+4) Dubai",
      "(GMT+11) Sydney",
      "(GMT+9) Seoul",
    ],
  },
]
---

<Combobox>
  <ComboboxInput placeholder="Select a timezone" />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No timezones found.</ComboboxEmpty>
      {timezones.map((group, index) => (
        <>
          <ComboboxGroup>
            <ComboboxLabel>{group.value}</ComboboxLabel>
            {group.items.map((item) => (
              <ComboboxItem value={item}>{item}</ComboboxItem>
            ))}
          </ComboboxGroup>
          {index < timezones.length - 1 && <ComboboxSeparator />}
        </>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Custom Items

You can render a custom component inside `ComboboxItem`. Set `data-label` on
the item to the text used for filtering and for the input after selection.

```astro title="src/components/examples/combobox/combobox-custom.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemTitle,
} from "@/components/ui/item"

const countries = [
  { code: "", value: "", continent: "", label: "Select country" },
  {
    code: "ar",
    value: "argentina",
    label: "Argentina",
    continent: "South America",
  },
  { code: "au", value: "australia", label: "Australia", continent: "Oceania" },
  { code: "br", value: "brazil", label: "Brazil", continent: "South America" },
  { code: "ca", value: "canada", label: "Canada", continent: "North America" },
  { code: "cn", value: "china", label: "China", continent: "Asia" },
  {
    code: "co",
    value: "colombia",
    label: "Colombia",
    continent: "South America",
  },
  { code: "eg", value: "egypt", label: "Egypt", continent: "Africa" },
  { code: "fr", value: "france", label: "France", continent: "Europe" },
  { code: "de", value: "germany", label: "Germany", continent: "Europe" },
  { code: "it", value: "italy", label: "Italy", continent: "Europe" },
  { code: "jp", value: "japan", label: "Japan", continent: "Asia" },
  { code: "ke", value: "kenya", label: "Kenya", continent: "Africa" },
  { code: "mx", value: "mexico", label: "Mexico", continent: "North America" },
  {
    code: "nz",
    value: "new-zealand",
    label: "New Zealand",
    continent: "Oceania",
  },
  { code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" },
  {
    code: "za",
    value: "south-africa",
    label: "South Africa",
    continent: "Africa",
  },
  { code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" },
  {
    code: "gb",
    value: "united-kingdom",
    label: "United Kingdom",
    continent: "Europe",
  },
  {
    code: "us",
    value: "united-states",
    label: "United States",
    continent: "North America",
  },
]
---

<Combobox>
  <ComboboxInput placeholder="Search countries..." />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No countries found.</ComboboxEmpty>
      {countries
        .filter((country) => country.code !== "")
        .map((country) => (
          <ComboboxItem value={country.value} data-label={country.label}>
            <Item size="xs" class="p-0">
              <ItemContent>
                <ItemTitle class="whitespace-nowrap">{country.label}</ItemTitle>
                <ItemDescription>
                  {country.continent} ({country.code})
                </ItemDescription>
              </ItemContent>
            </Item>
          </ComboboxItem>
        ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Invalid

Use the `aria-invalid` prop on `ComboboxInput` to make the combobox invalid.

```astro title="src/components/examples/combobox/combobox-invalid.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---

<Combobox>
  <ComboboxInput placeholder="Select a framework" aria-invalid="true" />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {frameworks.map((item) => (
        <ComboboxItem value={item}>{item}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Disabled

Use the `disabled` prop on `ComboboxInput` to disable the input and its
trigger, or on `Combobox` to disable all interaction.

```astro title="src/components/examples/combobox/combobox-disabled.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---

<Combobox>
  <ComboboxInput placeholder="Select a framework" disabled />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {frameworks.map((item) => (
        <ComboboxItem value={item}>{item}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Auto Highlight

Use the `autoHighlight` prop to automatically highlight the first item on filter.

```astro title="src/components/examples/combobox/combobox-auto-highlight.astro"
---
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---

<Combobox autoHighlight>
  <ComboboxInput placeholder="Select a framework" />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {frameworks.map((item) => (
        <ComboboxItem value={item}>{item}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Popup

Trigger the combobox from any button with `data-slot="combobox-trigger"` that
contains a `ComboboxValue`, and move `ComboboxInput` inside `ComboboxContent`
with `showTrigger={false}`.

```astro title="src/components/examples/combobox/combobox-popup.astro"
---
import ChevronDownIcon from "@lucide/astro/icons/chevron-down"

import { Button } from "@/components/ui/button"
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxValue,
} from "@/components/ui/combobox"

const countries = [
  { code: "", value: "", continent: "", label: "Select country" },
  {
    code: "ar",
    value: "argentina",
    label: "Argentina",
    continent: "South America",
  },
  { code: "au", value: "australia", label: "Australia", continent: "Oceania" },
  { code: "br", value: "brazil", label: "Brazil", continent: "South America" },
  { code: "ca", value: "canada", label: "Canada", continent: "North America" },
  { code: "cn", value: "china", label: "China", continent: "Asia" },
  {
    code: "co",
    value: "colombia",
    label: "Colombia",
    continent: "South America",
  },
  { code: "eg", value: "egypt", label: "Egypt", continent: "Africa" },
  { code: "fr", value: "france", label: "France", continent: "Europe" },
  { code: "de", value: "germany", label: "Germany", continent: "Europe" },
  { code: "it", value: "italy", label: "Italy", continent: "Europe" },
  { code: "jp", value: "japan", label: "Japan", continent: "Asia" },
  { code: "ke", value: "kenya", label: "Kenya", continent: "Africa" },
  { code: "mx", value: "mexico", label: "Mexico", continent: "North America" },
  {
    code: "nz",
    value: "new-zealand",
    label: "New Zealand",
    continent: "Oceania",
  },
  { code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" },
  {
    code: "za",
    value: "south-africa",
    label: "South Africa",
    continent: "Africa",
  },
  { code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" },
  {
    code: "gb",
    value: "united-kingdom",
    label: "United Kingdom",
    continent: "Europe",
  },
  {
    code: "us",
    value: "united-states",
    label: "United States",
    continent: "North America",
  },
]
---

<Combobox defaultValue={countries[0].value}>
  <Button
    variant="outline"
    data-slot="combobox-trigger"
    class="cn-combobox-trigger w-64 justify-between font-normal"
  >
    <ComboboxValue />
    <ChevronDownIcon class="cn-combobox-trigger-icon pointer-events-none" />
  </Button>
  <ComboboxContent>
    <ComboboxInput showTrigger={false} placeholder="Search" />
    <ComboboxList>
      <ComboboxEmpty>No items found.</ComboboxEmpty>
      {countries.map((item) => (
        <ComboboxItem value={item.value}>{item.label}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## Input Group

Add an addon by placing an `InputGroupAddon` inside `ComboboxInput`.

```astro title="src/components/examples/combobox/combobox-input-group.astro"
---
import GlobeIcon from "@lucide/astro/icons/globe"

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
} from "@/components/ui/combobox"
import { InputGroupAddon } from "@/components/ui/input-group"

const timezones = [
  {
    value: "Americas",
    items: [
      "(GMT-5) New York",
      "(GMT-8) Los Angeles",
      "(GMT-6) Chicago",
      "(GMT-5) Toronto",
      "(GMT-8) Vancouver",
      "(GMT-3) São Paulo",
    ],
  },
  {
    value: "Europe",
    items: [
      "(GMT+0) London",
      "(GMT+1) Paris",
      "(GMT+1) Berlin",
      "(GMT+1) Rome",
      "(GMT+1) Madrid",
      "(GMT+1) Amsterdam",
    ],
  },
  {
    value: "Asia/Pacific",
    items: [
      "(GMT+9) Tokyo",
      "(GMT+8) Shanghai",
      "(GMT+8) Singapore",
      "(GMT+4) Dubai",
      "(GMT+11) Sydney",
      "(GMT+9) Seoul",
    ],
  },
]
---

<Combobox>
  <ComboboxInput placeholder="Select a timezone">
    <InputGroupAddon>
      <GlobeIcon />
    </InputGroupAddon>
  </ComboboxInput>
  <ComboboxContent alignOffset={-28} class="w-60">
    <ComboboxList>
      <ComboboxEmpty>No timezones found.</ComboboxEmpty>
      {timezones.map((group) => (
        <ComboboxGroup>
          <ComboboxLabel>{group.value}</ComboboxLabel>
          {group.items.map((item) => (
            <ComboboxItem value={item}>{item}</ComboboxItem>
          ))}
        </ComboboxGroup>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
```

## API Reference

Source: [`src/components/ui/combobox`](https://github.com/fulldotdev/ui/tree/main/src/components/ui/combobox)

Interactivity comes from
[`@data-slot/combobox`](https://github.com/bejamas/data-slot/blob/main/packages/combobox/README.md).

| Component         | Prop                                                                                | Notes                                                 |
| ----------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `Combobox`        | `defaultValue`, `defaultOpen`, `placeholder`, `disabled`, `required`, `name`        | `name` adds a hidden input that submits the value     |
| `Combobox`        | `openOnFocus`, `autoHighlight`                                                      | `openOnFocus` defaults to `true`                      |
| `Combobox`        | `side`, `align`, `sideOffset`, `alignOffset`, `avoidCollisions`, `collisionPadding` | Popup placement, also accepted on `ComboboxContent`   |
| `ComboboxInput`   | `showTrigger` (`true`), `showClear` (`false`), `disabled`, input attributes         | Extra children render inside the input group          |
| `ComboboxItem`    | `value`, `disabled`, `showIndicator` (`true`), `data-label`                         | Without `data-label`, the item text is the label      |
| `ComboboxTrigger` | `showIcon` (`true`)                                                                 | Ghost icon button with a chevron                      |
| `ComboboxValue`   | span attributes                                                                     | Shows the selected label, or `placeholder` when empty |

The root emits `combobox:change`, `combobox:open-change` and
`combobox:input-change`, and accepts `combobox:set` with `value`, `open` or
`inputValue` in `detail`.

## Install what this page shows

```bash
npx shadcn@latest add @fulldev/combobox-examples
```
