shadcn-remix
GitHub

Combobox

Autocomplete input with a list of suggestions.

No items found.
Next.js
SvelteKit
Nuxt.js
Remix
Astro
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'

const frameworks = [
  'Next.js',
  'SvelteKit',
  'Nuxt.js',
  'Remix',
  'Astro',
] as const

export function ComboboxDemo() {
  return () => (
    <Combobox items={frameworks}>
      <ComboboxInput placeholder="Select a framework" />
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Installation

$ pnpm dlx shadcn@latest add https://www.shadcn-remix.com/r/combobox.json

Usage

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]

export function ExampleCombobox() {
  return (
    <Combobox items={frameworks}>
      <ComboboxInput placeholder="Select a framework" />
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Examples

Multiple

A combobox with multiple selection using multiple and ComboboxChips.

Next.js
No items found.
Next.js
SvelteKit
Nuxt.js
Remix
Astro
import { css } from 'remix/component'

import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
import { css } from 'remix/component'

import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
  ComboboxChipsInput,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxValue,
} from '#app/ui/combobox.tsx'

const frameworks = [
  'Next.js',
  'SvelteKit',
  'Nuxt.js',
  'Remix',
  'Astro',
] as const

export function ComboboxMultiple() {
  return () => (
    <Combobox multiple autoHighlight items={frameworks} defaultValue={[frameworks[0]]}>
      <ComboboxChips mix={css({ width: '100%', maxWidth: '20rem' })}>
        <ComboboxValue>
          {(values: string[]) => (
            <>
              {values.map((value) => (
                <ComboboxChip key={value}>{value}</ComboboxChip>
              ))}
              <ComboboxChipsInput />
            </>
          )}
        </ComboboxValue>
      </ComboboxChips>
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Clear Button

Use the showClear prop to show a clear button.

No items found.
Next.js
SvelteKit
Nuxt.js
Remix
Astro
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'

const frameworks = [
  'Next.js',
  'SvelteKit',
  'Nuxt.js',
  'Remix',
  'Astro',
] as const

export function ComboboxClear() {
  return () => (
    <Combobox items={frameworks} defaultValue={frameworks[0]}>
      <ComboboxInput placeholder="Select a framework" showClear />
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Groups

Use ComboboxGroup and ComboboxSeparator to group items.

No timezones found.
Americas
(GMT-5) New York
(GMT-8) Los Angeles
(GMT-6) Chicago
(GMT-5) Toronto
(GMT-8) Vancouver
(GMT-3) São Paulo
Europe
(GMT+0) London
(GMT+1) Paris
(GMT+1) Berlin
(GMT+1) Rome
(GMT+1) Madrid
(GMT+1) Amsterdam
Asia/Pacific
(GMT+9) Tokyo
(GMT+8) Shanghai
(GMT+8) Singapore
(GMT+4) Dubai
(GMT+11) Sydney
(GMT+9) Seoul
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
  ComboboxSeparator,
} from '#app/ui/combobox.tsx'

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',
    ],
  },
] as const

type TimezoneGroup = (typeof timezones)[number]

export function ComboboxGroups() {
  return () => (
    <Combobox items={timezones}>
      <ComboboxInput placeholder="Select a timezone" />
      <ComboboxContent>
        <ComboboxEmpty>No timezones found.</ComboboxEmpty>
        <ComboboxList>
          {(group: TimezoneGroup, index: number) => (
            <ComboboxGroup key={group.value} items={group.items}>
              <ComboboxLabel>{group.value}</ComboboxLabel>
              <ComboboxCollection>
                {(item: string) => (
                  <ComboboxItem key={item} value={item}>
                    {item}
                  </ComboboxItem>
                )}
              </ComboboxCollection>
              {index < timezones.length - 1 && <ComboboxSeparator />}
            </ComboboxGroup>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Custom Items

You can render a custom component inside ComboboxItem.

No countries found.
Argentina

South America (ar)

Australia

Oceania (au)

Brazil

South America (br)

Canada

North America (ca)

China

Asia (cn)

Colombia

South America (co)

Egypt

Africa (eg)

France

Europe (fr)

Germany

Europe (de)

Italy

Europe (it)

Japan

Asia (jp)

Kenya

Africa (ke)

Mexico

North America (mx)

New Zealand

Oceania (nz)

Nigeria

Africa (ng)

South Africa

Africa (za)

South Korea

Asia (kr)

United Kingdom

Europe (gb)

United States

North America (us)

import { css } from 'remix/component'

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
import { css } from 'remix/component'

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'
import { Item, ItemContent, ItemDescription, ItemTitle } from '#app/ui/item.tsx'

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',
  },
]

type Country = (typeof countries)[number]

export function ComboboxCustom() {
  return () => (
    <Combobox
      items={countries.filter((country) => country.code !== '')}
      itemToStringValue={(country: Country) => country.label}
    >
      <ComboboxInput placeholder="Search countries..." />
      <ComboboxContent>
        <ComboboxEmpty>No countries found.</ComboboxEmpty>
        <ComboboxList>
          {(country: Country) => (
            <ComboboxItem key={country.code} value={country}>
              <Item size="sm" mix={css({ padding: 0 })}>
                <ItemContent>
                  <ItemTitle mix={css({ whiteSpace: 'nowrap' })}>{country.label}</ItemTitle>
                  <ItemDescription>
                    {country.continent} ({country.code})
                  </ItemDescription>
                </ItemContent>
              </Item>
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Invalid

Use the aria-invalid prop to make the combobox invalid.

No items found.
Next.js
SvelteKit
Nuxt.js
Remix
Astro
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'

const frameworks = [
  'Next.js',
  'SvelteKit',
  'Nuxt.js',
  'Remix',
  'Astro',
] as const

export function ComboboxInvalid() {
  return () => (
    <Combobox items={frameworks}>
      <ComboboxInput placeholder="Select a framework" aria-invalid="true" />
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Disabled

Use the disabled prop to disable the combobox.

No items found.
Next.js
SvelteKit
Nuxt.js
Remix
Astro
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'

const frameworks = [
  'Next.js',
  'SvelteKit',
  'Nuxt.js',
  'Remix',
  'Astro',
] as const

export function ComboboxDisabled() {
  return () => (
    <Combobox items={frameworks}>
      <ComboboxInput placeholder="Select a framework" disabled />
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Auto Highlight

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

No items found.
Next.js
SvelteKit
Nuxt.js
Remix
Astro
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '#app/ui/combobox.tsx'

const frameworks = [
  'Next.js',
  'SvelteKit',
  'Nuxt.js',
  'Remix',
  'Astro',
] as const

export function ComboboxAutoHighlight() {
  return () => (
    <Combobox items={frameworks} autoHighlight>
      <ComboboxInput placeholder="Select a framework" />
      <ComboboxContent>
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

You can trigger the combobox from a button or any other component by using the render prop. Move the ComboboxInput inside the ComboboxContent.

No items found.
Select country
Argentina
Australia
Brazil
Canada
China
Colombia
Egypt
France
Germany
Italy
Japan
Kenya
Mexico
New Zealand
Nigeria
South Africa
South Korea
United Kingdom
United States
import { css } from 'remix/component'

import { buttonVariants } from '#app/ui/button.tsx'
import {
  Combobox,
  ComboboxContent,
import { css } from 'remix/component'

import { buttonVariants } from '#app/ui/button.tsx'
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxTrigger,
  ComboboxValue,
} from '#app/ui/combobox.tsx'

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',
  },
]

type Country = (typeof countries)[number]

export function ComboboxPopup() {
  return () => (
    <Combobox items={countries} defaultValue={countries[0]}>
      <ComboboxTrigger
        mix={[
          buttonVariants({ variant: 'outline' }),
          css({ width: '16rem', justifyContent: 'space-between', fontWeight: 400 }),
        ]}
      >
        <ComboboxValue />
      </ComboboxTrigger>
      <ComboboxContent>
        <ComboboxInput showTrigger={false} placeholder="Search" />
        <ComboboxEmpty>No items found.</ComboboxEmpty>
        <ComboboxList>
          {(item: Country) => (
            <ComboboxItem key={item.code} value={item}>
              {item.label}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

Input Group

You can add an addon to the combobox by using the InputGroupAddon component inside the ComboboxInput.

No timezones found.
Americas
(GMT-5) New York
(GMT-8) Los Angeles
(GMT-6) Chicago
(GMT-5) Toronto
(GMT-8) Vancouver
(GMT-3) São Paulo
Europe
(GMT+0) London
(GMT+1) Paris
(GMT+1) Berlin
(GMT+1) Rome
(GMT+1) Madrid
(GMT+1) Amsterdam
Asia/Pacific
(GMT+9) Tokyo
(GMT+8) Shanghai
(GMT+8) Singapore
(GMT+4) Dubai
(GMT+11) Sydney
(GMT+9) Seoul
import { css } from 'remix/component'

import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
import { css } from 'remix/component'

import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
} from '#app/ui/combobox.tsx'
import { GlobeIcon } from '#app/ui/icons.tsx'
import { InputGroupAddon } from '#app/ui/input-group.tsx'

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',
    ],
  },
] as const

type TimezoneGroup = (typeof timezones)[number]

export function ComboboxInputGroup() {
  return () => (
    <Combobox items={timezones}>
      <ComboboxInput placeholder="Select a timezone">
        <InputGroupAddon>
          <GlobeIcon />
        </InputGroupAddon>
      </ComboboxInput>
      <ComboboxContent alignOffset={-28} mix={css({ width: '15rem' })}>
        <ComboboxEmpty>No timezones found.</ComboboxEmpty>
        <ComboboxList>
          {(group: TimezoneGroup) => (
            <ComboboxGroup key={group.value} items={group.items}>
              <ComboboxLabel>{group.value}</ComboboxLabel>
              <ComboboxCollection>
                {(item: string) => (
                  <ComboboxItem key={item} value={item}>
                    {item}
                  </ComboboxItem>
                )}
              </ComboboxCollection>
            </ComboboxGroup>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

API Reference

Combobox

PropTypeDefault
itemsreadonly unknown[]-
multipleboolean-
valueunknown-
defaultValueunknown-
onValueChangeItemCallback<void, []>-
openboolean-
defaultOpenboolean-
onOpenChange((open: boolean) => void)-
inputValuestring-
defaultInputValuestring-
onInputValueChange((value: string) => void)-
autoHighlightboolean-
disabledboolean-
itemToStringLabelItemCallback<string, []>-
itemToStringValueItemCallback<string, []>-
filterItemCallback<boolean, [query: string]>-
namestring-

ComboboxInput

PropTypeDefault
type"text"-
mixMixValue-
showTriggerbooleantrue
showClearbooleanfalse

ComboboxTrigger

Toggles the popup. Without ComboboxInput it is the anchor too, e.g. a button-styled trigger: <ComboboxTrigger mix={buttonVariants({ variant: 'outline' })}><ComboboxValue /></ComboboxTrigger>.

PropTypeDefault
asInputGroupButtonboolean-
mixMixValue-

ComboboxClear

PropTypeDefault
mixMixValue-

ComboboxValue

PropTypeDefault
placeholderRemixNode-

ComboboxContent

PropTypeDefault
sideSide"bottom"
alignAlign"start"
sideOffsetnumber6
alignOffsetnumber0
mixMixValue-

ComboboxList

PropTypeDefault
mixMixValue-

ComboboxItem

PropTypeDefault
valueunknown-
disabledboolean-
mixMixValue-

ComboboxGroup

PropTypeDefault
itemsreadonly unknown[][]
mixMixValue-

ComboboxCollection

ComboboxLabel

PropTypeDefault
mixMixValue-

ComboboxEmpty

PropTypeDefault
mixMixValue-

ComboboxSeparator

PropTypeDefault
mixMixValue-

ComboboxChips

The chips container; it is also the popup's anchor (shadcn's useComboboxAnchor()).

PropTypeDefault
mixMixValue-

ComboboxChip

PropTypeDefault
valueunknown-
showRemovebooleantrue
mixMixValue-

ComboboxChipsInput

PropTypeDefault
mixMixValue-