Skip to content
Esc
navigateopen⌘Jpreview
On this page

Combobox

Autocomplete input and command palette with a list of suggestions.

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@/components/ui/combobox';

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

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

Install

npx shadcn@latest add @madeui/combobox

Usage

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@/components/ui/combobox';

Composition

Simple

A single-line input and a flat list.

<Combobox items={items}>
  <ComboboxInput />
  <ComboboxContent>
    <ComboboxEmpty />
    <ComboboxList>
      {(item) => <ComboboxItem value={item} />}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

With chips

Multi-select with multiple, chips, and a chips input. ComboboxChips also serves as the popup anchor — pass a ref to ComboboxContent’s anchor.

<Combobox items={items} multiple value={value} onValueChange={setValue}>
  <ComboboxChips ref={anchorRef}>
    <ComboboxValue>
      {(values) => values.map((value) => <ComboboxChip key={value}>{value}</ComboboxChip>)}
    </ComboboxValue>
    <ComboboxChipsInput />
  </ComboboxChips>
  <ComboboxContent anchor={anchorRef}>
    <ComboboxEmpty />
    <ComboboxList>
      {(item) => <ComboboxItem value={item} />}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

Multiple

Use multiple with ComboboxChips for multi-select. Selected values render as removable chips inline with the input.

'use client';

import * as React from 'react';

import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
  ComboboxChipsInput,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxValue,
} from '@/components/ui/combobox';

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

export default function ComboboxMultiple() {
  const [value, setValue] = React.useState<string[]>(['Next.js']);
  const anchorRef = React.useRef<HTMLDivElement>(null);

  return (
    <Combobox items={frameworks} multiple value={value} onValueChange={setValue}>
      <ComboboxChips ref={anchorRef}>
        <ComboboxValue>
          {(items: string[]) =>
            items.map((item) => <ComboboxChip key={item}>{item}</ComboboxChip>)
          }
        </ComboboxValue>
        <ComboboxChipsInput placeholder="Add framework…" />
      </ComboboxChips>
      <ComboboxContent anchor={anchorRef}>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

Trigger the combobox from a button-like ComboboxTrigger instead of an always-visible input — move ComboboxInput inside ComboboxContent with showTrigger={false}.

import * as stylex from '@stylexjs/stylex';

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxTrigger,
  ComboboxValue,
} from '@/components/ui/combobox';
import { space } from '@/lib/constants.stylex';

const fruits = ['Apple', 'Banana', 'Cherry', 'Mango', 'Peach'];

export default function ComboboxPopup() {
  return (
    <Combobox items={fruits}>
      <ComboboxTrigger>
        <ComboboxValue placeholder="Select a fruit" />
      </ComboboxTrigger>
      <ComboboxContent>
        <ComboboxInput
          showTrigger={false}
          placeholder="Search fruit…"
          style={styles.input}
        />
        <ComboboxEmpty>No fruit found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

const styles = stylex.create({
  input: {
    paddingBlock: space.s1,
    paddingInline: space.s1,
    width: '100%',
  },
});

Clear

Use showClear on ComboboxInput to show a button that clears the value.

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@/components/ui/combobox';

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

export default function ComboboxClear() {
  return (
    <Combobox items={frameworks} defaultValue="Next.js">
      <ComboboxInput showClear placeholder="Search framework…" />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(item: string) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

Groups

Use ComboboxGroup, ComboboxLabel, ComboboxCollection, and ComboboxSeparator to group related items.

import * as React from 'react';

import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
  ComboboxSeparator,
} from '@/components/ui/combobox';

const groups = [
  { label: 'Fruits', items: ['Apple', 'Banana', 'Cherry'] },
  { label: 'Vegetables', items: ['Carrot', 'Potato', 'Spinach'] },
];

export default function ComboboxGroups() {
  return (
    <Combobox items={groups}>
      <ComboboxInput placeholder="Search produce…" />
      <ComboboxContent>
        <ComboboxEmpty>No produce found.</ComboboxEmpty>
        <ComboboxList>
          {groups.map((group, index) => (
            <React.Fragment key={group.label}>
              {index > 0 && <ComboboxSeparator />}
              <ComboboxGroup items={group.items}>
                <ComboboxLabel>{group.label}</ComboboxLabel>
                <ComboboxCollection>
                  {(item: string) => (
                    <ComboboxItem key={item} value={item}>
                      {item}
                    </ComboboxItem>
                  )}
                </ComboboxCollection>
              </ComboboxGroup>
            </React.Fragment>
          ))}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

Custom items

Use itemToStringValue when items are objects, and render any content inside ComboboxItem.

import * as stylex from '@stylexjs/stylex';

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@/components/ui/combobox';
import { fontSize } from '@/lib/constants.stylex';
import { colors } from '@/lib/tokens.stylex';

interface Framework {
  label: string;
  value: string;
  description: string;
}

const frameworks: Framework[] = [
  {
    label: 'Next.js',
    value: 'next',
    description: 'The React framework for the web',
  },
  {
    label: 'SvelteKit',
    value: 'sveltekit',
    description: 'Web development, streamlined',
  },
  {
    label: 'Nuxt',
    value: 'nuxt',
    description: 'The intuitive Vue framework',
  },
];

export default function ComboboxCustomItems() {
  return (
    <Combobox
      items={frameworks}
      itemToStringValue={(framework: Framework) => framework.label}
    >
      <ComboboxInput placeholder="Search framework…" />
      <ComboboxContent>
        <ComboboxEmpty>No framework found.</ComboboxEmpty>
        <ComboboxList>
          {(framework: Framework) => (
            <ComboboxItem key={framework.value} value={framework}>
              <div {...stylex.props(styles.row)}>
                <span {...stylex.props(styles.title)}>{framework.label}</span>
                <span {...stylex.props(styles.description)}>
                  {framework.description}
                </span>
              </div>
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

const styles = stylex.create({
  row: {
    display: 'flex',
    flexDirection: 'column',
  },
  title: {
    fontSize: fontSize.sm,
  },
  description: {
    color: colors.mutedForeground,
    fontSize: fontSize.xs,
  },
});

Disabled

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@/components/ui/combobox';

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

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

Auto highlight

Use autoHighlight to highlight the first matching item automatically while filtering.

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@/components/ui/combobox';

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

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

API reference

Built on Base UI Combobox. Pass items to the root and it filters as you type out of the box; ComboboxList accepts a render function over the filtered items. All other props are forwarded to the underlying Base UI parts (multiple, value, onValueChange, itemToStringValue, autoHighlight, custom filter, …); see the Base UI Combobox API reference.

ComboboxInput

Text input with a built-in dropdown trigger button.

Prop Type Default Description
showTrigger boolean true Render the built-in dropdown trigger button. Set false when the input lives inside ComboboxContent and a ComboboxTrigger elsewhere anchors the popup.
showClear boolean false Render a clear button (Base UI Clear) before the trigger.
style StyleXStyles StyleX styles merged last — always win over the component’s own styles, applied to the wrapper.

ComboboxContent

Prop Type Default Description
side 'top' | 'right' | 'bottom' | 'left' 'bottom'
sideOffset number 6
align 'start' | 'center' | 'end' 'start'
alignOffset number 0
anchor Element | RefObject<Element | null> | (() => Element) Positions the popup against an element other than the input/trigger, e.g. ComboboxChips.
style StyleXStyles StyleX styles merged last — always win over the component’s own styles.

Styling

ComboboxTrigger (select-like popup trigger button), ComboboxChips (chips container, also usable as a popup anchor), ComboboxChip (pill with a built-in remove button), and ComboboxChipsInput (borderless input for inline use inside ComboboxChips) all accept style (StyleXStyles, merged last so caller overrides always win) plus all native props of the element they render.

ComboboxGroup, ComboboxLabel, ComboboxValue, and ComboboxCollection are also exported for grouped and multi-part lists.

Was this page helpful?