# Combobox

A closed-field picker that selects one or more options from a searchable overlay panel — the same shell the dropdown menu uses.

## Installation

```tsx
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxSearch,
  ComboboxSelectAll,
  ComboboxTrigger,
} from "@makeplane/propel/components/combobox";
```
## Usage

```tsx
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxSearch,
  ComboboxSelectAll,
  ComboboxSeparator,
  ComboboxStickyList,
  ComboboxTrigger,
} from "@makeplane/propel/components/combobox";
import { Field } from "@makeplane/propel/components/field";
import { Icon } from "@makeplane/propel/components/icon";
import { FieldLabel } from "@makeplane/propel/elements/field";
import { AddOutline } from "@makeplane/propel/icons";
import { useId, useState } from "react";

const REGIONS = ["us-central-1", "us-east-1", "eu-central-1", "ap-west-1"];

export default function BasicDemo() {
  const labelId = useId();
  const triggerId = useId();
  const [value, setValue] = useState<string[]>([]);
  const allSelected = REGIONS.every((region) => value.includes(region));
  const someSelected = !allSelected && value.length > 0;
  return (
    <Field name="region">
      <Combobox multiple items={REGIONS} value={value} onValueChange={setValue}>
        <FieldLabel id={labelId} size="lg" inset={false} htmlFor={triggerId}>
          Region
        </FieldLabel>
        <ComboboxTrigger
          id={triggerId}
          size="lg"
          placeholder="Select label"
          icon={<Icon icon={AddOutline} tint="placeholder" />}
        />
        <ComboboxContent
          aria-labelledby={labelId}
          search={
            <>
              <ComboboxSearch aria-label="Search" />
              <ComboboxStickyList>
                <ComboboxSelectAll
                  checked={allSelected}
                  indeterminate={someSelected}
                  onClick={() => setValue(allSelected ? [] : [...REGIONS])}
                />
                <ComboboxSeparator />
              </ComboboxStickyList>
            </>
          }
        >
          <ComboboxEmpty>No matches</ComboboxEmpty>
          <ComboboxList>
            {(region: string) => (
              <ComboboxItem key={region} value={region} selection="checkbox" label={region} />
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}
```
## Examples

### Variants

`variant` sets the frame's look on `ComboboxTrigger`, `ComboboxInputGroup`, `ComboboxChips` and
`ComboboxField`, with the same names as `SelectTrigger`: `secondary` is the bordered field surface
(the default), and `ghost` is borderless, filling only on hover, focus and while the popup is open.
Use `ghost` where the control sits inside an already-bordered container — a toolbar or a table cell
— so the frame does not double up.

`ComboboxTrigger` also takes `pill`, which keeps bordered chrome but hugs its value for compact
property and filter pickers. Like Propel Pill, it has no automatic chevron: an open popup shows as
its active chrome. Pair it with `ComboboxContent sizing="auto"` so the options are not squeezed to
the compact trigger's width. Its size scale is `xs`, `sm` and `md`, matching Propel Pill; the type
contract rejects those compact sizes on `secondary`/`ghost` and rejects `lg`–`2xl` on `pill`.

```tsx
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxSearch,
  ComboboxTrigger,
  type ComboboxTriggerVariant,
} from "@makeplane/propel/components/combobox";
import { Field } from "@makeplane/propel/components/field";
import { FieldLabel } from "@makeplane/propel/elements/field";
import { useId } from "react";

const REGIONS = ["us-central-1", "us-east-1", "eu-central-1", "ap-west-1"];

const VARIANTS: ComboboxTriggerVariant[] = ["secondary", "ghost", "pill"];

function VariantField({ variant }: { variant: ComboboxTriggerVariant }) {
  const labelId = useId();
  const triggerId = useId();
  return (
    <Field name={`region-${variant}`}>
      <Combobox items={REGIONS} multiple>
        <FieldLabel
          id={labelId}
          size={variant === "pill" ? "md" : "lg"}
          inset={false}
          htmlFor={triggerId}
        >
          {variant}
        </FieldLabel>
        {variant === "pill" ? (
          <ComboboxTrigger id={triggerId} size="md" variant="pill" placeholder="Select label" />
        ) : (
          <ComboboxTrigger id={triggerId} size="lg" variant={variant} placeholder="Select label" />
        )}
        <ComboboxContent
          aria-labelledby={labelId}
          sizing={variant === "pill" ? "auto" : undefined}
          search={<ComboboxSearch aria-label="Search" />}
        >
          <ComboboxEmpty>No matches</ComboboxEmpty>
          <ComboboxList>
            {(region: string) => (
              <ComboboxItem key={region} value={region} selection="checkbox" label={region} />
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}

export default function VariantsDemo() {
  return (
    <div className="flex flex-wrap items-end gap-3">
      {VARIANTS.map((variant) => (
        <VariantField key={variant} variant={variant} />
      ))}
    </div>
  );
}
```

### Trigger width

Field-style `ComboboxTrigger` controls use `width="auto"` by default, preserving the existing
288px minimum while allowing the trigger to grow. Use `width="fit"` with `secondary` or `ghost`
when the closed trigger should hug its icon, selected value, trailing content, chevron, gaps, and
padding. The popup keeps its own sizing behavior and can grow wider than the compact trigger. The
`pill` variant is already intrinsically sized and does not accept `width`.

```tsx
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxSearch,
  ComboboxTrigger,
  type ComboboxTriggerWidth,
} from "@makeplane/propel/components/combobox";
import { Field } from "@makeplane/propel/components/field";
import { Icon } from "@makeplane/propel/components/icon";
import { FieldLabel } from "@makeplane/propel/elements/field";
import { AddOutline } from "@makeplane/propel/icons";

const REGIONS = ["us-east-1", "A considerably longer deployment region"];
const WIDTHS: ComboboxTriggerWidth[] = ["auto", "fit"];

export default function FitWidthDemo() {
  return (
    <div className="flex flex-wrap items-end gap-4">
      {WIDTHS.map((width) => {
        const labelId = `combobox-${width}-label`;
        const triggerId = `combobox-${width}-trigger`;
        return (
          <Field key={width} name={`region-${width}`}>
            <Combobox items={REGIONS} defaultValue="us-east-1">
              <FieldLabel id={labelId} size="lg" inset={false} htmlFor={triggerId}>
                {width}
              </FieldLabel>
              <ComboboxTrigger
                id={triggerId}
                size="lg"
                width={width}
                icon={<Icon icon={AddOutline} tint="placeholder" />}
              />
              <ComboboxContent
                aria-labelledby={labelId}
                search={<ComboboxSearch aria-label="Search" />}
              >
                <ComboboxEmpty>No matches</ComboboxEmpty>
                <ComboboxList>
                  {(region: string) => <ComboboxItem key={region} value={region} label={region} />}
                </ComboboxList>
              </ComboboxContent>
            </Combobox>
          </Field>
        );
      })}
    </div>
  );
}
```

### Chips in the field

Default is already multi-select (closed trigger + overlay). Pass `multiple` with `ComboboxChips` when the filter input lives in the field: each selected value becomes a removable chip ahead of the inline input. Option rows use `selection="checkbox"` so they match the dropdown checkbox items.

```tsx
import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
} from "@makeplane/propel/components/combobox";
import { Field, FieldLabel } from "@makeplane/propel/components/field";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { CloseOutline } from "@makeplane/propel/icons";

const REGIONS = ["us-central-1", "us-east-1", "eu-central-1", "ap-west-1"];

export default function MultipleDemo() {
  return (
    <Field name="regions">
      <Combobox multiple items={REGIONS} defaultValue={["us-east-1", "eu-central-1"]}>
        <FieldLabel size="lg" inset={false}>
          Regions
        </FieldLabel>
        <ComboboxChips size="lg" placeholder="Add a region">
          {(region: string) => (
            <ComboboxChip
              key={region}
              label={region}
              remove={
                <IconButton
                  variant="ghost"
                  size="xs"
                  aria-label={`Remove ${region}`}
                  icon={<Icon icon={CloseOutline} />}
                />
              }
            />
          )}
        </ComboboxChips>
        <ComboboxContent>
          <ComboboxEmpty>No matches</ComboboxEmpty>
          <ComboboxList>
            {(region: string) => (
              <ComboboxItem key={region} value={region} selection="checkbox" label={region} />
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}
```

### Limiting visible chips

Chips wrap so the inline input stays usable at narrow widths. When a selection can grow large, `maxVisible` renders only the first N chips plus a "+N more" count (`maxVisible={0}` shows `N selected`); pass `overflowLabel` to localize it. Hidden values stay selected and are managed from the popup. Arrow keys and Backspace reach only rendered chips: Backspace removes the last rendered chip, or the last selected value when none are rendered.

The elements-tier `layout="collapse"` single-row frame is for custom compositions.

```tsx
import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
} from "@makeplane/propel/components/combobox";
import { Field, FieldLabel } from "@makeplane/propel/components/field";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { CloseOutline } from "@makeplane/propel/icons";

const REGIONS = [
  "us-central-1",
  "us-east-1",
  "eu-central-1",
  "eu-west-1",
  "ap-west-1",
  "ap-southeast-2",
  "sa-east-1",
];

export default function MaxVisibleDemo() {
  return (
    <Field name="regions">
      <Combobox multiple items={REGIONS} defaultValue={REGIONS}>
        <FieldLabel size="lg" inset={false}>
          Regions
        </FieldLabel>
        <ComboboxChips size="lg" placeholder="Add a region" maxVisible={2}>
          {(region: string) => (
            <ComboboxChip
              key={region}
              label={region}
              remove={
                <IconButton
                  variant="ghost"
                  size="xs"
                  aria-label={`Remove ${region}`}
                  icon={<Icon icon={CloseOutline} />}
                />
              }
            />
          )}
        </ComboboxChips>
        <ComboboxContent>
          <ComboboxEmpty>No matches</ComboboxEmpty>
          <ComboboxList>
            {(region: string) => (
              <ComboboxItem key={region} value={region} selection="checkbox" label={region} />
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}
```

### Submenu

`ComboboxSubmenu` nests a `ComboboxSubmenuContent` — a combobox in its own right, with its own
search and items — behind a `ComboboxSubmenuTrigger` row that selects nothing. The row goes in the
`trigger` prop rather than in `children`, because it belongs to the parent list. Point the nested
root's `value` / `onValueChange` at the parent's state when a nested pick should become the parent's
selection, as below, or at state of its own when it is a separate field.

### Grouped

```tsx
import {
  Combobox,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxGroupLabel,
  ComboboxInputGroup,
  ComboboxItem,
  ComboboxList,
} from "@makeplane/propel/components/combobox";
import { Field, FieldLabel } from "@makeplane/propel/components/field";

const GROUPED_REGIONS = [
  { label: "Americas", items: ["us-central-1", "us-east-1", "sa-east-1"] },
  { label: "Europe", items: ["eu-central-1", "eu-west-1"] },
  { label: "Asia Pacific", items: ["ap-west-1", "ap-southeast-2"] },
];

export default function GroupedDemo() {
  return (
    <Field name="region">
      <Combobox items={GROUPED_REGIONS}>
        <FieldLabel size="lg" inset={false}>
          Region
        </FieldLabel>
        <ComboboxInputGroup size="lg" placeholder="e.g. eu-central-1" />
        <ComboboxContent>
          <ComboboxEmpty>No matches</ComboboxEmpty>
          <ComboboxList>
            {(group: (typeof GROUPED_REGIONS)[number]) => (
              <ComboboxGroup key={group.label} items={group.items}>
                <ComboboxGroupLabel>{group.label}</ComboboxGroupLabel>
                <ComboboxCollection>
                  {(region: string) => <ComboboxItem key={region} value={region} label={region} />}
                </ComboboxCollection>
              </ComboboxGroup>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}
```

### Async search

`filter={null}` turns off built-in filtering so `onInputValueChange` can drive a remote search, with `ComboboxStatus` carrying the polite loading and empty-query hints. `useFilter` and `useFilteredItems` are re-exported from the same module for custom `filter` implementations.

```tsx
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInputGroup,
  ComboboxItem,
  ComboboxList,
  ComboboxStatus,
  useFilter,
} from "@makeplane/propel/components/combobox";
import { Field, FieldLabel } from "@makeplane/propel/components/field";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { CloseOutline } from "@makeplane/propel/icons";
import * as React from "react";

const MEMBERS = ["Aaditya Kapoor", "Bianca Ferreira", "Marcus Chen", "Priya Nair", "Rohan Sharma"];

export default function AsyncSearchDemo() {
  const [query, setQuery] = React.useState("");
  const [results, setResults] = React.useState<string[]>([]);
  const [pending, setPending] = React.useState(false);
  const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  const { contains } = useFilter();

  React.useEffect(() => () => clearTimeout(timeoutRef.current), []);

  const trimmed = query.trim();
  const status = pending ? "Searching…" : trimmed === "" ? "Start typing to search members" : null;

  return (
    <Field name="reviewer">
      <Combobox
        items={results}
        // Built-in filtering is off: the (fake) server already returns only the matches.
        filter={null}
        onInputValueChange={(nextQuery, { reason }) => {
          setQuery(nextQuery);
          if (reason === "item-press") {
            return;
          }
          clearTimeout(timeoutRef.current);
          const trimmedQuery = nextQuery.trim();
          if (trimmedQuery === "") {
            setResults([]);
            setPending(false);
            return;
          }
          setPending(true);
          // Deterministic stand-in for a server search: resolves after a short delay.
          timeoutRef.current = setTimeout(() => {
            setResults(MEMBERS.filter((member) => contains(member, trimmedQuery)));
            setPending(false);
          }, 300);
        }}
      >
        <FieldLabel size="lg" inset={false}>
          Reviewer
        </FieldLabel>
        <ComboboxInputGroup
          size="lg"
          placeholder="e.g. Priya"
          clear={
            <IconButton
              variant="ghost"
              size="md"
              aria-label="Clear reviewer"
              icon={<Icon icon={CloseOutline} />}
            />
          }
        />
        <ComboboxContent>
          <ComboboxStatus>{status}</ComboboxStatus>
          <ComboboxEmpty>{!pending && trimmed !== "" ? "No members match" : null}</ComboboxEmpty>
          <ComboboxList>
            {(member: string) => <ComboboxItem key={member} value={member} label={member} />}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}
```

### Sizes

`size` steps the trigger's height, text, and glyph scale: `md`, `lg`, `xl`, or `2xl`. `variant="ghost"` drops the bordered chrome; invalid on ghost has no trigger-level change (the danger cue is the helper text).

`ComboboxContent` opens inside the same overlay panel `MenuContent` uses. Pin a `ComboboxSearch` in that panel with `search={<ComboboxSearch />}` — that search row _is_ the combobox input, so don't also mount `ComboboxInputGroup` / `ComboboxChips`. Name the dialog with `aria-labelledby` pointing at the visible field label (or `aria-label`). The popup defaults to `sizing="anchor"` — the input's width, grown only by its content — while `sizing="auto"` sizes it by its rows between the 204px floor and the 400px ceiling, and `sizing="anchor-fixed"` keeps an exact anchor width for windowed lists (used by `ComboboxField`); see [Sizing](/foundations/sizing#popup-widths).

```tsx
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxItem,
  ComboboxList,
  ComboboxSearch,
  ComboboxTrigger,
} from "@makeplane/propel/components/combobox";
import { Field } from "@makeplane/propel/components/field";
import { FieldLabel } from "@makeplane/propel/elements/field";
import { useId } from "react";

const REGIONS = ["us-central-1", "us-east-1", "eu-central-1", "ap-west-1"];

const SIZES = ["md", "lg", "xl", "2xl"] as const;

function SizeField({ size }: { size: (typeof SIZES)[number] }) {
  const labelId = useId();
  const triggerId = useId();
  return (
    <Field name={`region-${size}`}>
      <Combobox items={REGIONS} multiple>
        <FieldLabel id={labelId} size="lg" inset={false} htmlFor={triggerId}>
          {size}
        </FieldLabel>
        <ComboboxTrigger id={triggerId} size={size} placeholder="Select label" />
        <ComboboxContent aria-labelledby={labelId} search={<ComboboxSearch aria-label="Search" />}>
          <ComboboxEmpty>No matches</ComboboxEmpty>
          <ComboboxList>
            {(region: string) => (
              <ComboboxItem key={region} value={region} selection="checkbox" label={region} />
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </Field>
  );
}

export default function SizesDemo() {
  return (
    <div className="flex flex-wrap items-end gap-3">
      {SIZES.map((size) => (
        <SizeField key={size} size={size} />
      ))}
    </div>
  );
}
```
## API Reference

### Combobox

The combobox Root — Base UI's context/state provider (renders no element of its own). A
behavior-only role, so it lives in `components` (rules 1a, 2); the styled parts live in
`elements/combobox` and are grafted onto Base UI behavior here.

Modal by default, unlike Base UI's root. `ComboboxSubmenu` roots stay non-modal — a nested
backdrop would cover the parent list.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `autoComplete` | `string` | — | Provides a hint to the browser for autofill. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete |
| `autoHighlight` | `boolean` | `false` | Whether the first matching item is highlighted automatically while filtering. |
| `highlightItemOnHover` | `boolean` | `true` | Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. |
| `itemToStringLabel` | `((itemValue: Value) => string)` | — | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for display in the input. If the shape of the object is `{ value, label }`, the label will be used automatically without needing to specify this prop. With a `createItems()` collection, this receives the derived value, and the collection's `getLabel` takes precedence for values it can resolve. |
| `itemToStringValue` | `((itemValue: Value) => string)` | — | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for form submission. If the shape of the object is `{ value, label }`, the value will be used automatically without needing to specify this prop. With a `createItems()` collection, this receives the derived value. |
| `isItemEqualToValue` | `((itemValue: Value, value: Value) => boolean)` | — | Custom comparison logic used to determine if a combobox item value matches the current selected value. Useful when item values are objects without matching referentially. With a `createItems()` collection, both arguments are derived values. Defaults to `Object.is` comparison. |
| `actionsRef` | `RefObject<Actions \| null>` | — | A ref to imperative actions. - `unmount`: Manually unmounts the combobox. Call this after any externally controlled closing animation finishes. |
| `onOpenChange` | `((open: boolean, eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the popup is opened or closed. |
| `onInputValueChange` | `((inputValue: string, eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the input value changes. |
| `onItemHighlighted` | `((highlightedValue: Value, eventDetails: HighlightEventDetails) => void)` | — | Callback fired when an item is highlighted or unhighlighted. Receives the highlighted item value (or `undefined` if no item is highlighted) and event details with a `reason` property describing why the highlight changed. The `reason` can be: - `'keyboard'`: the highlight changed due to keyboard navigation. - `'pointer'`: the highlight changed due to pointer hovering. - `'none'`: the highlight changed programmatically. |
| `multiple` | `boolean` | `false` | Whether multiple items can be selected. |
| `defaultValue` | `ComboboxValueType<Value, Multiple> \| null` | — | The uncontrolled selected value of the combobox when it's initially rendered. To render a controlled combobox, use the `value` prop instead. |
| `value` | `ComboboxValueType<Value, Multiple> \| null` | — | The selected value of the combobox. Use when controlled. |
| `onValueChange` | `((value: ComboboxValueType<Value, Multiple> \| (Multiple extends true ? never : null), eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the selected value of the combobox changes. |
| `modal` | `boolean` | `true` | Whether the combobox enters a modal state when open, like `Select`: page scroll is locked and a press outside the popup only dismisses it. The input (or input group) is cut out of the backdrop, so filter-in-field compositions still take typing and clicks. Set `false` to leave the rest of the page interactive while the list is open. |

### ComboboxTrigger

The ready-made closed-field trigger: grafts Base UI's `Trigger` onto the styled frame and shows
the selected value (or `placeholder`) plus a trailing chevron. The `pill` variant, like
`SelectTrigger`'s, has no automatic chevron: its open state shows as active chrome. Use this
instead of `ComboboxInputGroup` when the filter input lives in the panel (`ComboboxSearch`).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl"` | — | Trigger size on Pill's scale: `xs`, `sm` or `md`. Trigger size, from `md` to `2xl`: height, text and glyph sizing. |
| `placeholder` | `ReactNode` | — | Shown in the trigger while nothing is selected — Base UI's `Combobox.Value` placeholder. |
| `children` | `ReactNode` | — | Non-interactive selected-value preview. Omit to use Base UI's labels or placeholder. |
| `overflow` | `ReactNode` | — | Non-interactive overflow summary, kept visible when the value preview truncates. |
| `icon` | `ReactNode` | — | Decorative leading element at the inline-start, e.g. `<Icon icon={Plus} tint="placeholder" />`. |
| `trailing` | `ReactNode` | — | Decorative trailing glyph. Omit it to show a chevron (the Figma end node) — except on `pill`, which follows Pill anatomy and shows no glyph unless one is passed. This is not a nested popup-trigger control — the frame itself is the `Trigger`. |
| `variant` | `"secondary" \| "ghost" \| "pill"` | `"secondary"` | `pill` hugs its value with outline Pill chrome and shows no automatic chevron. Frame look. `secondary` is the bordered field surface; `ghost` is borderless. Invalid on ghost has no trigger chrome — the danger cue is the helper text below. |
| `width` | `"auto" \| "fit"` | — | `auto` keeps the 288px minimum; `fit` hugs the rendered trigger content. |

### ComboboxInputGroup

The ready-made single-select input frame: grafts Base UI's `InputGroup` onto the styled frame and
Base UI's `Input` onto the styled text field, laying out an optional leading `icon` slot and the
consumer-provided `clear`/`trigger` controls. `clear` stays `keepMounted` in a reserved slot so
the trailing trigger does not shift when the value is empty. When `trigger` is omitted, a
decorative chevron is shown. All remaining props pass through to the input (the element that
carries the combobox behavior and accessible name).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"2xl" \| "md" \| "lg" \| "xl"` | — | Visual size of the input row: height, text, and glyph sizing. Required. |
| `placeholder` | `string` | — | Input placeholder. |
| `variant` | `"secondary" \| "ghost"` | `"secondary"` | Frame look. `secondary` is the bordered field surface; `ghost` is borderless. |
| `icon` | `ReactNode` | — | Decorative leading element at the inline-start, e.g. `<Icon icon={Search} tint="placeholder" />`. |
| `clear` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | — | The clear control (e.g. an `IconButton`), grafted onto Base UI's `Clear` behavior. It carries its own — localizable — `aria-label`; the group bakes no label or glyph. |
| `trigger` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | — | The popup-trigger control (e.g. an `IconButton`), grafted onto Base UI's `Trigger` behavior. It carries its own — localizable — `aria-label`; the group bakes no label or glyph. Omit it to show a decorative chevron (the Figma end node) — the input still opens the list. |

### ComboboxChips

The ready-made multiselect input frame — it replaces `ComboboxInputGroup` in the `multiple`
anatomy. Grafts Base UI's `Chips` behavior onto the styled `elements/combobox` frame, owning only
the frame, the selected-values loop, and `maxVisible` overflow ahead of the inline input — each
chip's template is the consumer's `children`, mirroring `ComboboxList`. Chips wrap onto new rows
so the input stays usable. With `maxVisible`, the first N chips render followed by an overflow
count (`+N more`, or `N selected` when `maxVisible={0}`; pass `overflowLabel` to localize). Arrow
keys navigate rendered chips; Backspace removes the last rendered chip, or the last selected
value when none are rendered.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"2xl" \| "md" \| "lg" \| "xl"` | — | Visual size of the chips frame: height, text, and glyph sizing. Required. |
| `children` (required) | `(value: Value) => ReactNode` | — | Renders one selected value as a `ComboboxChip` — the `ComboboxChips` counterpart of `ComboboxList`'s item template. Set your own `key`, exactly like `ComboboxList`'s function child, e.g. `(value) => <ComboboxChip key={value} label={value} remove={<... />} />`. |
| `variant` | `"secondary" \| "ghost"` | `"secondary"` | Frame look. `secondary` is the bordered field surface; `ghost` is borderless. |
| `placeholder` | `string` | — | Input placeholder shown while typing to add another value. |
| `maxVisible` | `number` | `Infinity` | Cap on how many chips render, followed by an overflow count — set it when selections can grow large. Hidden values stay selected and are managed from the popup, but Backspace and arrow keys only reach rendered chips. A nonnegative integer; zero shows only the count. |
| `overflowLabel` | `((hiddenCount: number, details: { visibleCount: number; }) => ReactNode)` | `` (hiddenCount, { visibleCount }) => visibleCount > 0 ? `+${hiddenCount} more` : `${hiddenCount} selected` `` | Copy for the overflow count when `maxVisible` hides selected chips. Called with how many values are hidden and how many chips remain visible. Override to localize. |
| `trigger` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | — | The popup-trigger control (e.g. an `IconButton`), grafted onto Base UI's `Trigger` behavior. It carries its own — localizable — `aria-label`. Omit it to show a decorative chevron (the Figma end node) — the input still opens the list. |

### ComboboxChip

Ready-made chip: Base UI's chip behavior (arrow-key focus, Backspace/Delete removal) grafted onto
the styled tag, laying out an optional leading/trailing node around the label plus the
consumer-provided `remove` control. Base UI derives which selected value a chip represents from
its position among its siblings — pass one per value from `ComboboxChips`' `children`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | — | The chip's label. Also sets the chip's own accessible name. |
| `remove` (required) | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | — | The remove control, grafted onto Base UI's `ChipRemove` behavior. It carries its own — localizable — `aria-label`; the chip bakes no label or glyph. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxChipState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `size` | `"xs" \| "sm" \| "md" \| "lg"` | — | Chip height, text and glyph size. Inherited from the enclosing `ComboboxChips` when omitted. |
| `startContent` | `ReactNode` | — | Leading element before the label — an `<Icon icon={...} />`, an assignee `Avatar`, or similar. |
| `endContent` | `ReactNode` | — | Trailing element after the label, before the remove control, e.g. a status `<Icon .../>`. |

### ComboboxContent

The combobox list surface: Base UI portal + positioner, with `Combobox.Popup` grafted onto the
same elevated `OverlayPanel` `MenuContent` uses. Search/footer therefore live _inside_ the dialog
(unlike Menu, where they must sit outside `role="menu"`). The inner `ComboboxPopup` is
padding-only; sticky chrome stays pinned outside the scroll area.

When the input is in the panel (`ComboboxSearch` in `search`), the overlay is a dialog — name it
with `aria-labelledby` on the visible field label, or `aria-label`. Filter-in-field compositions
(`ComboboxInputGroup` / `ComboboxChips`) keep `role="presentation"` and need no dialog name.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `footer` | `ReactNode` | — | Sticky chrome pinned below the list. |
| `search` | `ReactNode` | — | Sticky chrome pinned above the list, e.g. a `ComboboxSearch`. When the input lives in this slot, the overlay is a `role="dialog"` and needs a name — pass `aria-labelledby` pointing at the visible field label, or `aria-label`. |
| `alignOffset` | `number \| OffsetFunction` | `0` | Additional offset in px along the align axis — slides the list along the anchor's edge without changing `align`. |
| `collisionPadding` | `Padding` | `16` | Minimum gap in px between the list and the viewport edge it would otherwise touch. |
| `collisionBoundary` | `Boundary` | — | The element or rect the list is confined to when avoiding collisions. Base UI's clipping ancestors when unset. |
| `collisionAvoidance` | `CollisionAvoidance` | — | What to do when the list would overflow the boundary, set per axis: flip to the other side, shift along it, or stay put. Base UI flips the side and shifts the alignment when unset. |
| `sticky` | `boolean` | `false` | Keep the list on screen after the anchor scrolls out of view, instead of following it out. |
| `positionMethod` | `"absolute" \| "fixed"` | `"absolute"` | Which CSS `position` the positioner uses. `fixed` escapes overflow-clipping ancestors, e.g. an input inside a sticky header. |
| `anchor` | `Element \| VirtualElement \| RefObject<Element \| null> \| (() => Element \| VirtualElement \| null) \| null` | — | Position against this element instead of the default anchor — an element, a ref, a virtual element, or a function returning one. Geometry only; the anchor keeps its role and interactions. |
| `disableAnchorTracking` | `boolean` | `false` | Stop tracking layout shifts of the anchor — position once, on open. |
| `viewportRef` | `Ref<HTMLDivElement>` | — | Ref to the scrolling viewport, for virtualized lists or scroll restoration. |
| `side` | `"top" \| "right" \| "bottom" \| "left" \| "inline-end" \| "inline-start"` | `bottom` | Which side of the input the list opens toward. |
| `sideOffset` | `number \| OffsetFunction` | `4` | Distance in px between the input and the list. |
| `align` | `"center" \| "start" \| "end"` | `start` | Alignment of the list relative to the input along `side`. |
| `sizing` | `"menu" \| "anchor" \| "auto" \| "anchor-fixed" \| "auto-fixed" \| "sm" \| "md" \| "lg"` | `anchor` | How the popup takes its width. `anchor` matches the input's width, grows only with its content up to the room beside the input. `auto` is the content's width instead — never narrower than 204px, never wider than 400px or the room beside the input, and held while open so a filtering list does not reflow. `anchor-fixed` keeps the exact anchor width for windowed lists and caps height at 288px so a mid-open available-height settle cannot shrink under the virtualizer. `auto-fixed` is `auto`'s width with the same 288px cap, for a windowed list under a compact trigger. |

### ComboboxSearch

A sticky search input pinned above a `ComboboxContent` list. Grafts Base UI's `Input` onto the
same search-row chrome `MenuSearch` uses, so the combobox panel matches the dropdown. This _is_
the combobox's `Input` — compose it via `ComboboxContent`'s `search` slot instead of
`ComboboxInputGroup` / `ComboboxChips`, which already mount an input in the field. Defaults to
`aria-label="Search"` (or the `placeholder`, if you changed that) so the combobox stays named.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `placeholder` | `string` | `Search` | Placeholder text. |
| `aria-label` | `string` | `"Search"` | Defines a string value that labels the current element. Accessible name for the search combobox. Placeholder is not a name — override to localize, or pass `aria-labelledby` instead. @see aria-labelledby. |

### ComboboxSelectAll

Sticky "select every item" row for a multi-select combobox panel. Not a `ComboboxItem` — it is
not a list option — so compose it in `ComboboxContent`'s `search` slot above the scrolling list.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` (required) | `boolean` | — | Whether every item is currently selected. |
| `onClick` (required) | `() => void` | — | Called when the row is activated. |
| `label` | `string` | `Select all` | Visible row label. |
| `indeterminate` | `boolean` | `false` | Whether only some (not all) items are currently selected — shows a dash instead of a check. Takes precedence over `checked` for both the glyph and `aria-checked="mixed"`. |
| `disabled` | `boolean` | — | Disables the row. |

### ComboboxStickyList

Pads sticky header rows (Select all + separator) with the same inset the scrolling list uses.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### ComboboxSeparator

A thin divider between groups of items in a combobox list — the same chrome as `MenuSeparator`.
Base UI's combobox has no Separator primitive, so this is the styled element composed into the
list.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### ComboboxList

The combobox items container — Base UI's `List` behavior part (it carries no propel styling of
its own), passed through so a full combobox composes without importing `@base-ui/react`. Inside
`ComboboxContent`'s overlay, this is the `role="listbox"` node; it defaults to
`aria-label="Suggestions"` so the listbox stays named (pass `aria-label` / `aria-labelledby` to
override or localize).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `aria-label` | `string` | `Suggestions` | Defines a string value that labels the current element. Accessible name for the listbox. Override to localize, or pass `aria-labelledby` instead. @see aria-labelledby. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxListState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### ComboboxItem

The ready-made option row: Base UI's `Item` behavior grafted onto the shared menu-row chrome so
the combobox list matches the dropdown panel. `selection="check"` reserves a TRAILING check
gutter (kept mounted while unselected); `selection="checkbox"` shows the LEADING checkbox box
used by multi-select.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxItemState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `variant` | `"neutral" \| "accent" \| "danger"` | `"neutral"` | Row look. `neutral` is the standard text hierarchy, `accent` marks the primary action, and `danger` marks a destructive one. |
| `selection` | `"checkbox" \| "check"` | `check` | Selection marker. `check` is the trailing accent check used by single-select; `checkbox` is the leading checkbox box used by multi-select (the Figma dropdown row). |
| `icon` | `ReactNode` | — | Leading element shown before the label, e.g. `<Icon icon={MapPin} tint="secondary" />`. On a `selection="checkbox"` row it follows the checkbox box. |
| `trailing` | `ReactNode` | — | Content pinned at the row's inline end, e.g. a count. |
| `value` | `unknown` | — | Option value whose string form is used when `label` is omitted. Option value. Object values cannot derive display text, so they require `label`. |
| `label` | `string` | — | Option label. Defaults to the string form of `value`. Visible option label. Required when `value` is not a primitive. |

### ComboboxSubmenu

One nested combobox: the parent row that opens it, plus its own Base UI `Combobox.Root`. The row
itself carries no value — it is a way into the nested list, and only that list's items are
selectable — so the two roots keep separate values. Point this root's `value`/`onValueChange` at
the parent's state when the nested picks should land in the parent's selection, or at state of
its own when they should not.

Base UI has no combobox submenu primitive (only `Menu` does), so the nesting is assembled here.
The nested popup still portals to `<body>`, but it stays a React-tree descendant of the parent
popup, which is what stops a press inside it from reading as an outside-press that dismisses the
parent.

Selecting a nested item closes the nested list (Base UI's own single-select behavior) and leaves
the parent list open. Closing the parent too is the consumer's call — do it from `onValueChange`,
where the parent's own `open` is in reach.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `trigger` (required) | `ReactNode` | — | The parent row that opens this nested list — a `ComboboxSubmenuTrigger`. It is a **prop**, not a child, because it belongs to the PARENT list: the row is one of that combobox's options (so it stays arrow-key navigable and filterable there) while everything under `children` belongs to the nested combobox this part roots. Rendering it here keeps it above that nested root rather than inside it. |
| `autoComplete` | `string` | — | Provides a hint to the browser for autofill. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete |
| `autoHighlight` | `boolean` | `false` | Whether the first matching item is highlighted automatically while filtering. |
| `highlightItemOnHover` | `boolean` | `true` | Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. |
| `itemToStringLabel` | `((itemValue: Value) => string)` | — | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for display in the input. If the shape of the object is `{ value, label }`, the label will be used automatically without needing to specify this prop. With a `createItems()` collection, this receives the derived value, and the collection's `getLabel` takes precedence for values it can resolve. |
| `itemToStringValue` | `((itemValue: Value) => string)` | — | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for form submission. If the shape of the object is `{ value, label }`, the value will be used automatically without needing to specify this prop. With a `createItems()` collection, this receives the derived value. |
| `isItemEqualToValue` | `((itemValue: Value, value: Value) => boolean)` | — | Custom comparison logic used to determine if a combobox item value matches the current selected value. Useful when item values are objects without matching referentially. With a `createItems()` collection, both arguments are derived values. Defaults to `Object.is` comparison. |
| `actionsRef` | `RefObject<Actions \| null>` | — | A ref to imperative actions. - `unmount`: Manually unmounts the combobox. Call this after any externally controlled closing animation finishes. |
| `onInputValueChange` | `((inputValue: string, eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the input value changes. |
| `onItemHighlighted` | `((highlightedValue: Value, eventDetails: HighlightEventDetails) => void)` | — | Callback fired when an item is highlighted or unhighlighted. Receives the highlighted item value (or `undefined` if no item is highlighted) and event details with a `reason` property describing why the highlight changed. The `reason` can be: - `'keyboard'`: the highlight changed due to keyboard navigation. - `'pointer'`: the highlight changed due to pointer hovering. - `'none'`: the highlight changed programmatically. |
| `multiple` | `boolean` | `false` | Whether multiple items can be selected. |
| `defaultValue` | `ComboboxValueType<Value, Multiple> \| null` | — | The uncontrolled selected value of the combobox when it's initially rendered. To render a controlled combobox, use the `value` prop instead. |
| `value` | `ComboboxValueType<Value, Multiple> \| null` | — | The selected value of the combobox. Use when controlled. |
| `onValueChange` | `((value: ComboboxValueType<Value, Multiple> \| (Multiple extends true ? never : null), eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the selected value of the combobox changes. |
| `open` | `boolean` | — | Whether the nested list is open. Controlled; pair with `onOpenChange`. |
| `defaultOpen` | `boolean` | `false` | Whether the nested list is open on mount. Uncontrolled. |
| `onOpenChange` | `((open: boolean, eventDetails?: ChangeEventDetails) => void)` | — | Called with the next open state when the nested list opens or closes. `eventDetails` is Base UI's own — forwarded verbatim, so `reason` still separates an `escapeKey` close from an `outsidePress` or a completed `itemPress`. It is optional here, unlike on a Base UI root, because propel drives the hover and row-press paths itself (Base UI ships no combobox submenu to drive them) and exposes no way to mint details for them: those two calls arrive with the flag alone. |
| `openOnHover` | `boolean` | `true` | Open the nested list when the pointer rests on the parent row, the way a menu submenu does. Turn it off for lists the user sweeps while reading, where an unasked-for search field is a nuisance — the row still opens on click or on Enter while highlighted. |
| `delay` | `number` | `100` | How long in ms the pointer must rest on the row before a hover-open fires. |
| `closeDelay` | `number` | `150` | How long in ms the nested list stays open after the pointer leaves both it and the row. The grace period is what lets the pointer cross the gap between the two. |
| `closeParentOnEsc` | `boolean` | `false` | Whether `Escape` inside the nested list closes the whole stack, or only this list. Mirrors `MenuSubmenu`'s prop of the same name — and its default, which steps back out to the parent rather than dismissing everything. |
| `children` | `ReactNode` | — | The nested `ComboboxSubmenuContent` (and nothing else — the row goes in `trigger`). |

### ComboboxSubmenuTrigger

The parent row that opens a nested combobox. It is an option of the PARENT list — Base UI's
`Combobox.Item` behavior, so it keeps that list's highlight, arrow-key navigation and filtering —
but it carries **no value**: the pointer and `Enter` paths both suppress Base UI's own selection
(`preventBaseUIHandler`) and open the nested list instead. Only the nested list's items select.

Pass it to `ComboboxSubmenu`'s `trigger` prop rather than rendering it as a child, so it resolves
against the parent combobox's context instead of the nested root's.

It still registers with the parent list as an item, so a parent that derives its options from the
DOM rather than an `items` prop counts this row among them. Give such a parent an explicit
`items` list when that matters (a `ComboboxSelectAll`, for instance, should not count it).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | — | Row label — the name of the nested list, not of a value. |
| `variant` | `"neutral" \| "accent" \| "danger"` | `"neutral"` | Row look. `neutral` is the standard text hierarchy, `accent` marks the primary action, and `danger` marks a destructive one. |
| `icon` | `ReactNode` | — | Leading element shown before the label, e.g. `<Icon icon={Users} tint="secondary" />`. |
| `trailing` | `ReactNode` | — | Content pinned before the submenu chevron, e.g. a count. |

### ComboboxSubmenuContent

The floating surface for a nested combobox — the same elevated, scrollable panel as
`ComboboxContent`, but anchored beside its parent row instead of below the field. Place it inside
a `ComboboxSubmenu`, which supplies both the row it anchors to and the nested root it belongs
to.

It has no `anchor` prop: the parent row is the anchor, and overriding it would leave the row
highlighted next to a panel somewhere else.

Same naming contract as `ComboboxContent`: a nested list with its own `ComboboxSearch` is a
`role="dialog"` and must be named — pass `aria-label` (the parent row's label reads well) or
`aria-labelledby`. Name its `ComboboxList` too, since that list, not the row, is what a screen
reader lands in when the nested combobox opens.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `footer` | `ReactNode` | — | Sticky chrome pinned below the list. |
| `search` | `ReactNode` | — | Sticky chrome pinned above the list, e.g. a `ComboboxSearch`. When the input lives in this slot, the overlay is a `role="dialog"` and needs a name — pass `aria-labelledby` pointing at the visible field label, or `aria-label`. |
| `alignOffset` | `number \| OffsetFunction` | `0` | Additional offset in px along the align axis — slides the list along the anchor's edge without changing `align`. |
| `collisionPadding` | `Padding` | `16` | Minimum gap in px between the list and the viewport edge it would otherwise touch. |
| `collisionBoundary` | `Boundary` | — | The element or rect the list is confined to when avoiding collisions. Base UI's clipping ancestors when unset. |
| `collisionAvoidance` | `CollisionAvoidance` | — | What to do when the list would overflow the boundary, set per axis: flip to the other side, shift along it, or stay put. Base UI flips the side and shifts the alignment when unset. |
| `sticky` | `boolean` | `false` | Keep the list on screen after the anchor scrolls out of view, instead of following it out. |
| `positionMethod` | `"absolute" \| "fixed"` | `"absolute"` | Which CSS `position` the positioner uses. `fixed` escapes overflow-clipping ancestors, e.g. an input inside a sticky header. |
| `disableAnchorTracking` | `boolean` | `false` | Stop tracking layout shifts of the anchor — position once, on open. |
| `viewportRef` | `Ref<HTMLDivElement>` | — | Ref to the scrolling viewport, for virtualized lists or scroll restoration. |
| `side` | `"top" \| "right" \| "bottom" \| "left" \| "inline-end" \| "inline-start"` | `right` | Which side of the parent row the nested list opens toward. |
| `sideOffset` | `number \| OffsetFunction` | `4` | Distance in px between the parent row and the nested list. |
| `align` | `"center" \| "start" \| "end"` | `start` | Alignment of the nested list relative to the parent row along `side`. |
| `sizing` | `"menu" \| "anchor" \| "auto" \| "sm" \| "md" \| "lg"` | `auto` | How the nested popup takes its width. `auto` is the content's width — never narrower than 204px, never wider than 400px or the room beside the row, and held while open so a filtering list does not reflow. `anchor` matches the parent row's width instead, which is rarely what a nested list wants. `anchor-fixed` and `auto-fixed` are not accepted here; they are `ComboboxContent`-only. |

### ComboboxItemIndicator

Ready-made combobox item indicator: Base UI's selection behavior grafted onto the styled marker,
with a default check when no children are given (defaults are a `components` concern). Composed
at the row's inline-end. Mounted on every row by default, so a selection change never
re-truncates a label and the reserved column holds while unselected (the styled marker hides its
glyph off `data-selected`).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxItemIndicatorState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### ComboboxEmpty

The ready-made empty-state row: Base UI's `Empty` behavior grafted onto the styled
`elements/combobox` row. Pass the — localizable — no-matches message as children.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxEmptyState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### ComboboxStatus

A polite live region inside the popup for async hints ("Searching…", "12 results") — Base UI's
`Status` behavior grafted onto the shared muted listbox hint styling.

No component-specific props — accepts the standard HTML attributes for the element it renders.

### ComboboxGroup

Groups related options under a `ComboboxGroupLabel`. Pass the group's `items` so the nested
`ComboboxCollection` renders (and filters) just this group's options.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxGroupState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### ComboboxGroupLabel

The muted heading naming a `ComboboxGroup`, with optional `meta` pinned at its end. Base UI's
`GroupLabel` behavior is grafted onto the heading's TITLE, not the whole row, so the group's
accessible name is the heading text alone — a count or a "Clear" button in `meta` stays out of
it. Place it inside the `ComboboxGroup` it names, so Base UI wires it as that group's label.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `meta` | `ReactNode` | — | Content pinned at the heading's inline end, e.g. a count or a "Clear" action. |

### ComboboxCollection

Renders the filtered items of the nearest `ComboboxGroup` (or the root) through a function child
— the grouped counterpart of `ComboboxList`'s function child.

No component-specific props — accepts the standard HTML attributes for the element it renders.

### ComboboxRow

A grid row wrapper for multi-column listbox layouts (pass `cols` on the root's `grid`).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ComboboxRowState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
