# Combobox
URL: /design/components/combobox

Type to filter, then pick: a select with a search field.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

\[interactive preview component ComboboxSpecimen omitted]

Code for ComboboxSpecimen preview:

```tsx
"use client";

import { useState, type ReactNode } from "react";
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
} from "@/components/ui/combobox";

function ComboboxSpecimen(): ReactNode {
  const runtimes = [
    "AI SDK",
    "LangGraph",
    "LangChain",
    "Mastra",
    "External Store",
    "Local",
  ];
  return (
    <Combobox items={runtimes} defaultValue="AI SDK">
      <ComboboxInput
        className="w-56"
        placeholder="Search runtimes…"
        aria-label="Search runtimes"
      />
      <ComboboxContent>
        <ComboboxEmpty>No runtimes found.</ComboboxEmpty>
        <ComboboxList>
          {(runtime: string) => (
            <ComboboxItem key={runtime} value={runtime}>
              {runtime}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}
```

Both flavors wrap Base UI's Combobox; Radix ships no combobox primitive. Typing narrows the list against the `items` you pass to the root, and when nothing matches the popup shows its empty state.

## Installation

Install the dependencies:

- packages

  - @base-ui/react

Copy the source into `components/ui/combobox.tsx`. Registry items that depend on it install it automatically.

- code

  "use client"; import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox"; import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"; import { cn } from "@/lib/utils"; const Combobox = ComboboxPrimitive.Root; function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { return \<ComboboxPrimitive.Value data-slot="combobox-value" {...props} />; } function ComboboxTrigger({ className, ...props }: ComboboxPrimitive.Trigger.Props) { return ( \<ComboboxPrimitive.Trigger data-slot="combobox-trigger" className={cn( "text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 grid size-6 shrink-0 cursor-default place-items-center rounded-md outline-none focus-visible:ring-1 data-disabled:pointer-events-none", className, )} {...props} > \<ChevronDownIcon className="size-4" /> \<span className="sr-only">Open\</span> \</ComboboxPrimitive.Trigger> ); } function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { return ( \<ComboboxPrimitive.Clear data-slot="combobox-clear" className={cn( "text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 grid size-6 shrink-0 cursor-default place-items-center rounded-md outline-none focus-visible:ring-1 data-disabled:pointer-events-none", className, )} {...props} > \<XIcon className="size-4" /> \<span className="sr-only">Clear\</span> \</ComboboxPrimitive.Clear> ); } function ComboboxInput({ className, showTrigger = true, showClear = false, ...props }: ComboboxPrimitive.Input.Props & { showTrigger?: boolean; showClear?: boolean; }) { return ( \<div data-slot="combobox-input-wrapper" className={cn("relative w-full", className)} > \<ComboboxPrimitive.Input data-slot="combobox-input" className="placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 bg-muted/60 focus-visible:bg-background aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-9 w-full rounded-lg border border-transparent px-3 pe-9 text-base transition-colors outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-1 md:text-sm" {...props} /> {(showTrigger || showClear) && ( \<div className="absolute inset-y-0 end-1.5 flex items-center"> {showClear && \<ComboboxClear className="peer/clear" />} {showTrigger && ( \<ComboboxTrigger className={cn(showClear && "peer-data-visible/clear:hidden")} /> )} \</div> )} \</div> ); } function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props }: ComboboxPrimitive.Popup.Props & Pick< ComboboxPrimitive.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor" >) { return ( \<ComboboxPrimitive.Portal> \<ComboboxPrimitive.Positioner side={side} sideOffset={sideOffset} align={align} alignOffset={alignOffset} anchor={anchor} className="isolate z-50" > \<ComboboxPrimitive.Popup data-slot="combobox-content" className={cn( "group/combobox-content bg-popover text-popover-foreground data-\[side=bottom]:slide-in-from-top-2 data-\[side=left]:slide-in-from-right-2 data-\[side=right]:slide-in-from-left-2 data-\[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 ring-foreground/10 relative w-(--anchor-width) max-w-(--available-width) origin-(--transform-origin) overflow-hidden rounded-\[var(--radius-surface,var(--radius-lg))] ring-1 duration-100 outline-none", className, )} {...props} /> \</ComboboxPrimitive.Positioner> \</ComboboxPrimitive.Portal> ); } function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { return ( \<ComboboxPrimitive.List data-slot="combobox-list" className={cn( "max-h-\[min(--spacing(96),var(--available-height))] scroll-py-1.5 overflow-y-auto p-1.5 data-empty:p-0", className, )} {...props} /> ); } function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) { return ( \<ComboboxPrimitive.Item data-slot="combobox-item" className={cn( "data-highlighted:bg-foreground/\[0.06] relative flex w-full cursor-default items-center gap-2 rounded-lg py-1.5 ps-2 pe-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 \[&\_svg]:pointer-events-none \[&\_svg]:shrink-0 \[&\_svg:not(\[class\*='size-'])]:size-4", className, )} {...props} > {children} \<ComboboxPrimitive.ItemIndicator data-slot="combobox-item-indicator" render={ \<span className="pointer-events-none absolute end-2 flex size-4 items-center justify-center" /> } > \<CheckIcon className="size-4" /> \</ComboboxPrimitive.ItemIndicator> \</ComboboxPrimitive.Item> ); } function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { return ( \<ComboboxPrimitive.Group data-slot="combobox-group" className={cn(className)} {...props} /> ); } function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) { return ( \<ComboboxPrimitive.GroupLabel data-slot="combobox-label" className={cn( "text-muted-foreground px-2 py-1.5 font-mono text-\[11px] font-medium tracking-wide uppercase", className, )} {...props} /> ); } function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { return ( \<ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} /> ); } function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { return ( \<ComboboxPrimitive.Empty data-slot="combobox-empty" className={cn( "text-muted-foreground hidden w-full justify-center py-4 text-center text-sm group-data-empty/combobox-content:flex", className, )} {...props} /> ); } function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) { return ( \<ComboboxPrimitive.Separator data-slot="combobox-separator" className={cn("bg-foreground/10 -mx-1.5 my-1 h-px", className)} {...props} /> ); } export { Combobox, ComboboxValue, ComboboxTrigger, ComboboxClear, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, };

* title

  components/ui/combobox.tsx

* copyText

  "use client"; import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox"; import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"; import { cn } from "@/lib/utils"; const Combobox = ComboboxPrimitive.Root; function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { return \<ComboboxPrimitive.Value data-slot="combobox-value" {...props} />; } function ComboboxTrigger({ className, ...props }: ComboboxPrimitive.Trigger.Props) { return ( \<ComboboxPrimitive.Trigger data-slot="combobox-trigger" className={cn( "text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 grid size-6 shrink-0 cursor-default place-items-center rounded-md outline-none focus-visible:ring-1 data-disabled:pointer-events-none", className, )} {...props} > \<ChevronDownIcon className="size-4" /> \<span className="sr-only">Open\</span> \</ComboboxPrimitive.Trigger> ); } function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { return ( \<ComboboxPrimitive.Clear data-slot="combobox-clear" className={cn( "text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 grid size-6 shrink-0 cursor-default place-items-center rounded-md outline-none focus-visible:ring-1 data-disabled:pointer-events-none", className, )} {...props} > \<XIcon className="size-4" /> \<span className="sr-only">Clear\</span> \</ComboboxPrimitive.Clear> ); } function ComboboxInput({ className, showTrigger = true, showClear = false, ...props }: ComboboxPrimitive.Input.Props & { showTrigger?: boolean; showClear?: boolean; }) { return ( \<div data-slot="combobox-input-wrapper" className={cn("relative w-full", className)} > \<ComboboxPrimitive.Input data-slot="combobox-input" className="placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 bg-muted/60 focus-visible:bg-background aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-9 w-full rounded-lg border border-transparent px-3 pe-9 text-base transition-colors outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-1 md:text-sm" {...props} /> {(showTrigger || showClear) && ( \<div className="absolute inset-y-0 end-1.5 flex items-center"> {showClear && \<ComboboxClear className="peer/clear" />} {showTrigger && ( \<ComboboxTrigger className={cn(showClear && "peer-data-visible/clear:hidden")} /> )} \</div> )} \</div> ); } function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props }: ComboboxPrimitive.Popup.Props & Pick< ComboboxPrimitive.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor" >) { return ( \<ComboboxPrimitive.Portal> \<ComboboxPrimitive.Positioner side={side} sideOffset={sideOffset} align={align} alignOffset={alignOffset} anchor={anchor} className="isolate z-50" > \<ComboboxPrimitive.Popup data-slot="combobox-content" className={cn( "group/combobox-content bg-popover text-popover-foreground data-\[side=bottom]:slide-in-from-top-2 data-\[side=left]:slide-in-from-right-2 data-\[side=right]:slide-in-from-left-2 data-\[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 ring-foreground/10 relative w-(--anchor-width) max-w-(--available-width) origin-(--transform-origin) overflow-hidden rounded-\[var(--radius-surface,var(--radius-lg))] ring-1 duration-100 outline-none", className, )} {...props} /> \</ComboboxPrimitive.Positioner> \</ComboboxPrimitive.Portal> ); } function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { return ( \<ComboboxPrimitive.List data-slot="combobox-list" className={cn( "max-h-\[min(--spacing(96),var(--available-height))] scroll-py-1.5 overflow-y-auto p-1.5 data-empty:p-0", className, )} {...props} /> ); } function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) { return ( \<ComboboxPrimitive.Item data-slot="combobox-item" className={cn( "data-highlighted:bg-foreground/\[0.06] relative flex w-full cursor-default items-center gap-2 rounded-lg py-1.5 ps-2 pe-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 \[&\_svg]:pointer-events-none \[&\_svg]:shrink-0 \[&\_svg:not(\[class\*='size-'])]:size-4", className, )} {...props} > {children} \<ComboboxPrimitive.ItemIndicator data-slot="combobox-item-indicator" render={ \<span className="pointer-events-none absolute end-2 flex size-4 items-center justify-center" /> } > \<CheckIcon className="size-4" /> \</ComboboxPrimitive.ItemIndicator> \</ComboboxPrimitive.Item> ); } function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { return ( \<ComboboxPrimitive.Group data-slot="combobox-group" className={cn(className)} {...props} /> ); } function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) { return ( \<ComboboxPrimitive.GroupLabel data-slot="combobox-label" className={cn( "text-muted-foreground px-2 py-1.5 font-mono text-\[11px] font-medium tracking-wide uppercase", className, )} {...props} /> ); } function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { return ( \<ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} /> ); } function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { return ( \<ComboboxPrimitive.Empty data-slot="combobox-empty" className={cn( "text-muted-foreground hidden w-full justify-center py-4 text-center text-sm group-data-empty/combobox-content:flex", className, )} {...props} /> ); } function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) { return ( \<ComboboxPrimitive.Separator data-slot="combobox-separator" className={cn("bg-foreground/10 -mx-1.5 my-1 h-px", className)} {...props} /> ); } export { Combobox, ComboboxValue, ComboboxTrigger, ComboboxClear, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, };

* viewportClassName

  max-h-\[450px]

- language

  tsx

- code

  "use client"; import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox"; import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"; import { cn } from "@/lib/utils"; const Combobox = ComboboxPrimitive.Root; function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { return \<ComboboxPrimitive.Value data-slot="combobox-value" {...props} />; } function ComboboxTrigger({ className, ...props }: ComboboxPrimitive.Trigger.Props) { return ( \<ComboboxPrimitive.Trigger data-slot="combobox-trigger" className={cn( "text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 grid size-6 shrink-0 cursor-default place-items-center rounded-md outline-none focus-visible:ring-1 data-disabled:pointer-events-none", className, )} {...props} > \<ChevronDownIcon className="size-4" /> \<span className="sr-only">Open\</span> \</ComboboxPrimitive.Trigger> ); } function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { return ( \<ComboboxPrimitive.Clear data-slot="combobox-clear" className={cn( "text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 grid size-6 shrink-0 cursor-default place-items-center rounded-md outline-none focus-visible:ring-1 data-disabled:pointer-events-none", className, )} {...props} > \<XIcon className="size-4" /> \<span className="sr-only">Clear\</span> \</ComboboxPrimitive.Clear> ); } function ComboboxInput({ className, showTrigger = true, showClear = false, ...props }: ComboboxPrimitive.Input.Props & { showTrigger?: boolean; showClear?: boolean; }) { return ( \<div data-slot="combobox-input-wrapper" className={cn("relative w-full", className)} > \<ComboboxPrimitive.Input data-slot="combobox-input" className="placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 bg-muted/60 focus-visible:bg-background aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-9 w-full rounded-lg border border-transparent px-3 pe-9 text-base transition-colors outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-1 md:text-sm" {...props} /> {(showTrigger || showClear) && ( \<div className="absolute inset-y-0 end-1.5 flex items-center"> {showClear && \<ComboboxClear className="peer/clear" />} {showTrigger && ( \<ComboboxTrigger className={cn(showClear && "peer-data-visible/clear:hidden")} /> )} \</div> )} \</div> ); } function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props }: ComboboxPrimitive.Popup.Props & Pick< ComboboxPrimitive.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor" >) { return ( \<ComboboxPrimitive.Portal> \<ComboboxPrimitive.Positioner side={side} sideOffset={sideOffset} align={align} alignOffset={alignOffset} anchor={anchor} className="isolate z-50" > \<ComboboxPrimitive.Popup data-slot="combobox-content" className={cn( "group/combobox-content bg-popover text-popover-foreground data-\[side=bottom]:slide-in-from-top-2 data-\[side=left]:slide-in-from-right-2 data-\[side=right]:slide-in-from-left-2 data-\[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 ring-foreground/10 relative w-(--anchor-width) max-w-(--available-width) origin-(--transform-origin) overflow-hidden rounded-\[var(--radius-surface,var(--radius-lg))] ring-1 duration-100 outline-none", className, )} {...props} /> \</ComboboxPrimitive.Positioner> \</ComboboxPrimitive.Portal> ); } function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { return ( \<ComboboxPrimitive.List data-slot="combobox-list" className={cn( "max-h-\[min(--spacing(96),var(--available-height))] scroll-py-1.5 overflow-y-auto p-1.5 data-empty:p-0", className, )} {...props} /> ); } function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) { return ( \<ComboboxPrimitive.Item data-slot="combobox-item" className={cn( "data-highlighted:bg-foreground/\[0.06] relative flex w-full cursor-default items-center gap-2 rounded-lg py-1.5 ps-2 pe-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 \[&\_svg]:pointer-events-none \[&\_svg]:shrink-0 \[&\_svg:not(\[class\*='size-'])]:size-4", className, )} {...props} > {children} \<ComboboxPrimitive.ItemIndicator data-slot="combobox-item-indicator" render={ \<span className="pointer-events-none absolute end-2 flex size-4 items-center justify-center" /> } > \<CheckIcon className="size-4" /> \</ComboboxPrimitive.ItemIndicator> \</ComboboxPrimitive.Item> ); } function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { return ( \<ComboboxPrimitive.Group data-slot="combobox-group" className={cn(className)} {...props} /> ); } function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) { return ( \<ComboboxPrimitive.GroupLabel data-slot="combobox-label" className={cn( "text-muted-foreground px-2 py-1.5 font-mono text-\[11px] font-medium tracking-wide uppercase", className, )} {...props} /> ); } function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { return ( \<ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} /> ); } function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { return ( \<ComboboxPrimitive.Empty data-slot="combobox-empty" className={cn( "text-muted-foreground hidden w-full justify-center py-4 text-center text-sm group-data-empty/combobox-content:flex", className, )} {...props} /> ); } function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) { return ( \<ComboboxPrimitive.Separator data-slot="combobox-separator" className={cn("bg-foreground/10 -mx-1.5 my-1 h-px", className)} {...props} /> ); } export { Combobox, ComboboxValue, ComboboxTrigger, ComboboxClear, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, };

## Usage

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

const runtimes = ["AI SDK", "LangGraph", "LangChain", "Mastra"];

export function RuntimePicker() {
  return (
    <Combobox items={runtimes}>
      <ComboboxInput placeholder="Search runtimes…" />
      <ComboboxContent>
        <ComboboxEmpty>No runtimes found.</ComboboxEmpty>
        <ComboboxList>
          {(runtime: string) => (
            <ComboboxItem key={runtime} value={runtime}>
              {runtime}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}
```

## Examples

### Groups

Pass an array of `{ value, items }` groups to the root, then render each group with its own collection. Group labels use the mono eyebrow voice.

\[interactive preview component ComboboxGroupsSpecimen omitted]

Code for ComboboxGroupsSpecimen preview:

```tsx
"use client";

import { useState, type ReactNode } from "react";
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
} from "@/components/ui/combobox";

function ComboboxGroupsSpecimen(): ReactNode {
  const groups = [
    {
      value: "Frameworks",
      items: ["AI SDK", "LangGraph", "LangChain", "Mastra"],
    },
    { value: "Custom", items: ["External Store", "Local"] },
  ];
  return (
    <Combobox items={groups}>
      <ComboboxInput
        className="w-56"
        placeholder="Search runtimes…"
        aria-label="Search runtimes"
      />
      <ComboboxContent>
        <ComboboxEmpty>No runtimes found.</ComboboxEmpty>
        <ComboboxList>
          {(group: { value: string; items: string[] }) => (
            <ComboboxGroup key={group.value} items={group.items}>
              <ComboboxLabel>{group.value}</ComboboxLabel>
              <ComboboxCollection>
                {(runtime: string) => (
                  <ComboboxItem key={runtime} value={runtime}>
                    {runtime}
                  </ComboboxItem>
                )}
              </ComboboxCollection>
            </ComboboxGroup>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}
```

### Clear and disabled

`showClear` shows a clear button instead of the chevron while a value is selected. A disabled combobox keeps its value visible at half opacity.

\[interactive preview component ComboboxStatesSpecimen omitted]

Code for ComboboxStatesSpecimen preview:

```tsx
"use client";

import { useState, type ReactNode } from "react";
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
} from "@/components/ui/combobox";

function ComboboxStatesSpecimen(): ReactNode {
  const runtimes = ["AI SDK", "LangGraph", "LangChain", "Mastra"];
  return (
    <Combobox items={runtimes} defaultValue="LangGraph">
      <ComboboxInput
        className="w-56"
        showClear
        placeholder="Search runtimes…"
        aria-label="Search runtimes"
      />
      <ComboboxContent>
        <ComboboxEmpty>No runtimes found.</ComboboxEmpty>
        <ComboboxList>
          {(runtime: string) => (
            <ComboboxItem key={runtime} value={runtime}>
              {runtime}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
    <Combobox items={runtimes} defaultValue="AI SDK" disabled>
      <ComboboxInput
        className="w-56"
        placeholder="Search runtimes…"
        aria-label="Search runtimes"
      />
      <ComboboxContent>
        <ComboboxEmpty>No runtimes found.</ComboboxEmpty>
        <ComboboxList>
          {(runtime: string) => (
            <ComboboxItem key={runtime} value={runtime}>
              {runtime}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}
```

## API Reference

### Combobox

Wraps Base UI's Combobox root and accepts its props.

- `items?`: `readonly T[] | readonly Group<T>[]` — The options to filter and list. Either a flat array or an array of { value, items } groups.
- `value?`: `T` — Controlled selected value.
- `onValueChange?`: `(value: T, details) => void` — Called when the selection changes.
- `defaultValue?`: `T` — Uncontrolled initial selection.
- `disabled`: `boolean` (default `false`) — Prevents interaction.

### ComboboxInput

The field that anchors the popup and receives the filter text.

- `showTrigger`: `boolean` (default `true`) — Renders the chevron button at the end of the field.
- `showClear`: `boolean` (default `false`) — Adds a clear button that replaces the chevron while a value is selected.
- `placeholder?`: `string` — Hint shown while the field is empty.

### ComboboxContent

The positioned popup that holds the list.

- `side`: `"top" | "right" | "bottom" | "left"` (default `"bottom"`) — Which side of the field the popup opens on.
- `align`: `"start" | "center" | "end"` (default `"start"`) — Alignment against the field.
- `sideOffset`: `number` (default `6`) — Distance from the field in pixels.

### ComboboxItem

- `value`: `T` — The value this option represents.
- `disabled`: `boolean` (default `false`) — Prevents selecting this option.