moduix

Combobox

An input combined with a filterable list of predefined values.

API Reference

Original primitive API

Behavior, accessibility details, and low-level props are documented by Base UI.

Base UI API

Choosing the right component

Use Combobox when selection should come from predefined options, but users still need an input to search or narrow that list.

  • Choose Combobox for searchable selection from a known dataset.
  • Choose Autocomplete when free-form input is valid and suggestions are optional guidance.
  • Choose Select when input typing is unnecessary and a button-triggered list is enough.

Basic

Choose fruit
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxInputGroup,  ComboboxInput,  ComboboxControlActions,  ComboboxClear,  ComboboxTrigger,  ComboboxContent,  ComboboxEmpty,  ComboboxList,  ComboboxItem,  ComboboxItemIndicator,  ComboboxItemText,} from "moduix";import { useId } from "react";export function ComboboxDemo() {  const id = useId();  return (    <Combobox items={fruits} itemToStringLabel={(item) => item.label}>      <ComboboxField>        <ComboboxFieldLabel htmlFor={id}>Choose fruit</ComboboxFieldLabel>        <ComboboxInputGroup>          <ComboboxInput id={id} placeholder="e.g. Mango" />          <ComboboxControlActions>            <ComboboxClear aria-label="Clear selection" />            <ComboboxTrigger aria-label="Open options" />          </ComboboxControlActions>        </ComboboxInputGroup>      </ComboboxField>      <ComboboxContent>        <ComboboxEmpty>No fruits found.</ComboboxEmpty>        <ComboboxList>          {(item) => (            <ComboboxItem key={item.id} value={item}>              <ComboboxItemIndicator />              <ComboboxItemText>{item.label}</ComboboxItemText>            </ComboboxItem>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
const fruits = [  { id: "apple", label: "Apple", value: "apple" },  { id: "banana", label: "Banana", value: "banana" },  { id: "mango", label: "Mango", value: "mango" },];

Full list of component variables available for project-level overrides.

PropertyDefaultDescription
--combobox-action-bgtransparentDefault: transparent.
--combobox-action-bg-hovervar(--color-muted)Default: var(--color-muted).
--combobox-action-color-hovervar(--color-foreground)Default: var(--color-foreground).
--combobox-action-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-action-size1.5remDefault: 1.5rem.
--combobox-actions-gap0.125remDefault: 0.125rem.
--combobox-actions-offset-right0.5remDefault: 0.5rem.
--combobox-arrow-height0.625remDefault: 0.625rem.
--combobox-arrow-inline-offset13pxDefault: 13px.
--combobox-arrow-size8pxDefault: 8px.
--combobox-arrow-stroke-colorvar(--combobox-popup-border-color)Default: var(--combobox-popup-border-color).
--combobox-arrow-width1.25remDefault: 1.25rem.
--combobox-backdrop-bgvar(--backdrop-bg, var(--color-overlay))Default: var(--backdrop-bg, var(--color-overlay)).
--combobox-backdrop-blur2pxDefault: 2px.
--combobox-backdrop-transitionvar(--transition-default)Default: var(--transition-default).
--combobox-bgvar(--color-background)Default: var(--color-background).
--combobox-border-colorvar(--color-border)Default: var(--color-border).
--combobox-border-widthvar(--border-width-sm)Default: var(--border-width-sm).
--combobox-check-padding-x-start0.625remDefault: 0.625rem.
--combobox-chip-bgvar(--color-muted)Default: var(--color-muted).
--combobox-chip-colorvar(--color-foreground)Default: var(--color-foreground).
--combobox-chip-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-chip-gapvar(--spacing-1)Default: var(--spacing-1).
--combobox-chip-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-chip-min-height1.625remDefault: 1.625rem.
--combobox-chip-padding-left0.5remDefault: 0.5rem.
--combobox-chip-padding-right0.375remDefault: 0.375rem.
--combobox-chip-padding-y0.1875remDefault: 0.1875rem.
--combobox-chip-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-chip-remove-bg-hovervar(--color-overlay-foreground)Default: var(--color-overlay-foreground).
--combobox-chip-remove-icon-size0.75remDefault: 0.75rem.
--combobox-chip-remove-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-chip-remove-size1remDefault: 1rem.
--combobox-chips-gapvar(--spacing-1)Default: var(--spacing-1).
--combobox-chips-input-height1.75remDefault: 1.75rem.
--combobox-chips-input-min-width4remDefault: 4rem.
--combobox-chips-input-padding-x0.5remDefault: 0.5rem.
--combobox-chips-paddingvar(--spacing-1)Default: var(--spacing-1).
--combobox-colorvar(--color-foreground)Default: var(--color-foreground).
--combobox-control-heightvar(--size-lg)Default: var(--size-lg).
--combobox-empty-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-empty-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-empty-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-empty-padding-x1remDefault: 1rem.
--combobox-empty-padding-y0.75remDefault: 0.75rem.
--combobox-field-gap0.375remDefault: 0.375rem.
--combobox-focus-ring-colorvar(--color-ring)Default: var(--color-ring).
--combobox-group-label-bgvar(--color-popover)Default: var(--color-popover).
--combobox-group-label-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-group-label-font-sizevar(--text-xs)Default: var(--text-xs).
--combobox-group-label-font-weightvar(--weight-semibold)Default: var(--weight-semibold).
--combobox-group-label-line-heightvar(--line-height-text-xs)Default: var(--line-height-text-xs).
--combobox-group-label-padding-bottom0.35remDefault: 0.35rem.
--combobox-group-label-padding-top0.35remDefault: 0.35rem.
--combobox-group-label-padding-x0.625remDefault: 0.625rem.
--combobox-group-padding-bottomvar(--spacing-1)Default: var(--spacing-1).
--combobox-highlight-bgvar(--color-foreground)Default: var(--color-foreground).
--combobox-highlight-colorvar(--color-background)Default: var(--color-background).
--combobox-highlight-inset-xvar(--spacing-1)Default: var(--spacing-1).
--combobox-highlight-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-icon-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-icon-size0.875remDefault: 0.875rem.
--combobox-icon-svg-size1remDefault: 1rem.
--combobox-inline-input-container-padding-bottomvar(--spacing-2)Default: var(--spacing-2).
--combobox-inline-input-container-padding-topvar(--spacing-2)Default: var(--spacing-2).
--combobox-inline-input-container-padding-xvar(--spacing-2)Default: var(--spacing-2).
--combobox-inline-input-divider-colorvar(--combobox-border-color)Default: var(--combobox-border-color).
--combobox-inline-input-divider-stylesolidDefault: solid.
--combobox-inline-input-divider-widthvar(--border-width-sm)Default: var(--border-width-sm).
--combobox-input-group-padding-x0Default: 0.
--combobox-input-padding-x-end3.25remDefault: 3.25rem.
--combobox-input-padding-x-start0.875remDefault: 0.875rem.
--combobox-input-placeholder-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-item-bgtransparentDefault: transparent.
--combobox-item-border-colortransparentDefault: transparent.
--combobox-item-border-radius0Default: 0.
--combobox-item-border-width0Default: 0.
--combobox-item-colorvar(--color-foreground)Default: var(--color-foreground).
--combobox-item-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-item-gapvar(--spacing-2)Default: var(--spacing-2).
--combobox-item-indicator-bgtransparentDefault: transparent.
--combobox-item-indicator-border-colortransparentDefault: transparent.
--combobox-item-indicator-border-width0Default: 0.
--combobox-item-indicator-icon-size0.75remDefault: 0.75rem.
--combobox-item-indicator-padding0Default: 0.
--combobox-item-indicator-radius0Default: 0.
--combobox-item-indicator-size0.75remDefault: 0.75rem.
--combobox-item-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-item-min-height2remDefault: 2rem.
--combobox-item-padding-x-end1remDefault: 1rem.
--combobox-item-padding-yvar(--spacing-2)Default: var(--spacing-2).
--combobox-item-text-content-gapvar(--spacing-2)Default: var(--spacing-2).
--combobox-item-text-icon-colorcurrentColorDefault: currentColor.
--combobox-item-text-icon-size1remDefault: 1rem.
--combobox-label-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-label-font-weightvar(--weight-medium)Default: var(--weight-medium).
--combobox-label-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-list-max-heightvar(--combobox-popup-max-height)Default: var(--combobox-popup-max-height).
--combobox-list-padding-y0.25remDefault: 0.25rem.
--combobox-list-scroll-padding-y0.25remDefault: 0.25rem.
--combobox-popup-bgvar(--color-popover)Default: var(--color-popover).
--combobox-popup-border-colorvar(--color-border)Default: var(--color-border).
--combobox-popup-max-height24remDefault: 24rem.
--combobox-radiusvar(--radius-md)Default: var(--radius-md).
--combobox-separator-margin-x1remDefault: 1rem.
--combobox-separator-margin-y0.375remDefault: 0.375rem.
--combobox-shadowvar(--shadow-lg)Default: var(--shadow-lg).
--combobox-status-colorvar(--combobox-empty-color)Default: var(--combobox-empty-color).
--combobox-status-font-sizevar(--combobox-empty-font-size)Default: var(--combobox-empty-font-size).
--combobox-status-gapvar(--spacing-2)Default: var(--spacing-2).
--combobox-status-line-heightvar(--combobox-empty-line-height)Default: var(--combobox-empty-line-height).
--combobox-status-padding-xvar(--combobox-empty-padding-x)Default: var(--combobox-empty-padding-x).
--combobox-status-padding-yvar(--combobox-empty-padding-y)Default: var(--combobox-empty-padding-y).
--combobox-width16remDefault: 16rem.

Interactive variables scoped for docs preview without changing size scale tokens.

PropertyValueDefaultDescription
--combobox-action-bgtransparentDefault: transparent.
--combobox-action-bg-hovervar(--color-muted)Default: var(--color-muted).
--combobox-action-color-hovervar(--color-foreground)Default: var(--color-foreground).
--combobox-action-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-action-size1.5remDefault: 1.5rem.
--combobox-actions-gap0.125remDefault: 0.125rem.
--combobox-actions-offset-right0.5remDefault: 0.5rem.
--combobox-arrow-height0.625remDefault: 0.625rem.
--combobox-arrow-inline-offset13pxDefault: 13px.
--combobox-arrow-size8pxDefault: 8px.
--combobox-arrow-stroke-colorvar(--combobox-popup-border-color)Default: var(--combobox-popup-border-color).
--combobox-arrow-width1.25remDefault: 1.25rem.
--combobox-backdrop-bgvar(--backdrop-bg, var(--color-overlay))Default: var(--backdrop-bg, var(--color-overlay)).
--combobox-backdrop-blur2pxDefault: 2px.
--combobox-backdrop-transitionvar(--transition-default)Default: var(--transition-default).
--combobox-bgvar(--color-background)Default: var(--color-background).
--combobox-border-colorvar(--color-border)Default: var(--color-border).
--combobox-border-widthvar(--border-width-sm)Default: var(--border-width-sm).
--combobox-check-padding-x-start0.625remDefault: 0.625rem.
--combobox-chip-bgvar(--color-muted)Default: var(--color-muted).
--combobox-chip-colorvar(--color-foreground)Default: var(--color-foreground).
--combobox-chip-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-chip-gapvar(--spacing-1)Default: var(--spacing-1).
--combobox-chip-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-chip-min-height1.625remDefault: 1.625rem.
--combobox-chip-padding-left0.5remDefault: 0.5rem.
--combobox-chip-padding-right0.375remDefault: 0.375rem.
--combobox-chip-padding-y0.1875remDefault: 0.1875rem.
--combobox-chip-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-chip-remove-bg-hovervar(--color-overlay-foreground)Default: var(--color-overlay-foreground).
--combobox-chip-remove-icon-size0.75remDefault: 0.75rem.
--combobox-chip-remove-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-chip-remove-size1remDefault: 1rem.
--combobox-chips-gapvar(--spacing-1)Default: var(--spacing-1).
--combobox-chips-input-height1.75remDefault: 1.75rem.
--combobox-chips-input-min-width4remDefault: 4rem.
--combobox-chips-input-padding-x0.5remDefault: 0.5rem.
--combobox-chips-paddingvar(--spacing-1)Default: var(--spacing-1).
--combobox-colorvar(--color-foreground)Default: var(--color-foreground).
--combobox-control-heightvar(--size-lg)Default: var(--size-lg).
--combobox-empty-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-empty-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-empty-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-empty-padding-x1remDefault: 1rem.
--combobox-empty-padding-y0.75remDefault: 0.75rem.
--combobox-field-gap0.375remDefault: 0.375rem.
--combobox-focus-ring-colorvar(--color-ring)Default: var(--color-ring).
--combobox-group-label-bgvar(--color-popover)Default: var(--color-popover).
--combobox-group-label-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-group-label-font-sizevar(--text-xs)Default: var(--text-xs).
--combobox-group-label-font-weightvar(--weight-semibold)Default: var(--weight-semibold).
--combobox-group-label-line-heightvar(--line-height-text-xs)Default: var(--line-height-text-xs).
--combobox-group-label-padding-bottom0.35remDefault: 0.35rem.
--combobox-group-label-padding-top0.35remDefault: 0.35rem.
--combobox-group-label-padding-x0.625remDefault: 0.625rem.
--combobox-group-padding-bottomvar(--spacing-1)Default: var(--spacing-1).
--combobox-highlight-bgvar(--color-foreground)Default: var(--color-foreground).
--combobox-highlight-colorvar(--color-background)Default: var(--color-background).
--combobox-highlight-inset-xvar(--spacing-1)Default: var(--spacing-1).
--combobox-highlight-radiusvar(--radius-sm)Default: var(--radius-sm).
--combobox-icon-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-icon-size0.875remDefault: 0.875rem.
--combobox-icon-svg-size1remDefault: 1rem.
--combobox-inline-input-container-padding-bottomvar(--spacing-2)Default: var(--spacing-2).
--combobox-inline-input-container-padding-topvar(--spacing-2)Default: var(--spacing-2).
--combobox-inline-input-container-padding-xvar(--spacing-2)Default: var(--spacing-2).
--combobox-inline-input-divider-colorvar(--combobox-border-color)Default: var(--combobox-border-color).
--combobox-inline-input-divider-stylesolidDefault: solid.
--combobox-inline-input-divider-widthvar(--border-width-sm)Default: var(--border-width-sm).
--combobox-input-group-padding-x0Default: 0.
--combobox-input-padding-x-end3.25remDefault: 3.25rem.
--combobox-input-padding-x-start0.875remDefault: 0.875rem.
--combobox-input-placeholder-colorvar(--color-muted-foreground)Default: var(--color-muted-foreground).
--combobox-item-bgtransparentDefault: transparent.
--combobox-item-border-colortransparentDefault: transparent.
--combobox-item-border-radius0Default: 0.
--combobox-item-border-width0Default: 0.
--combobox-item-colorvar(--color-foreground)Default: var(--color-foreground).
--combobox-item-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-item-gapvar(--spacing-2)Default: var(--spacing-2).
--combobox-item-indicator-bgtransparentDefault: transparent.
--combobox-item-indicator-border-colortransparentDefault: transparent.
--combobox-item-indicator-border-width0Default: 0.
--combobox-item-indicator-icon-size0.75remDefault: 0.75rem.
--combobox-item-indicator-padding0Default: 0.
--combobox-item-indicator-radius0Default: 0.
--combobox-item-indicator-size0.75remDefault: 0.75rem.
--combobox-item-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-item-min-height2remDefault: 2rem.
--combobox-item-padding-x-end1remDefault: 1rem.
--combobox-item-padding-yvar(--spacing-2)Default: var(--spacing-2).
--combobox-item-text-content-gapvar(--spacing-2)Default: var(--spacing-2).
--combobox-item-text-icon-colorcurrentColorDefault: currentColor.
--combobox-item-text-icon-size1remDefault: 1rem.
--combobox-label-font-sizevar(--text-sm)Default: var(--text-sm).
--combobox-label-font-weightvar(--weight-medium)Default: var(--weight-medium).
--combobox-label-line-heightvar(--line-height-text-sm)Default: var(--line-height-text-sm).
--combobox-list-max-heightvar(--combobox-popup-max-height)Default: var(--combobox-popup-max-height).
--combobox-list-padding-y0.25remDefault: 0.25rem.
--combobox-list-scroll-padding-y0.25remDefault: 0.25rem.
--combobox-popup-bgvar(--color-popover)Default: var(--color-popover).
--combobox-popup-border-colorvar(--color-border)Default: var(--color-border).
--combobox-popup-max-height24remDefault: 24rem.
--combobox-radiusvar(--radius-md)Default: var(--radius-md).
--combobox-separator-margin-x1remDefault: 1rem.
--combobox-separator-margin-y0.375remDefault: 0.375rem.
--combobox-shadowvar(--shadow-lg)Default: var(--shadow-lg).
--combobox-status-colorvar(--combobox-empty-color)Default: var(--combobox-empty-color).
--combobox-status-font-sizevar(--combobox-empty-font-size)Default: var(--combobox-empty-font-size).
--combobox-status-gapvar(--spacing-2)Default: var(--spacing-2).
--combobox-status-line-heightvar(--combobox-empty-line-height)Default: var(--combobox-empty-line-height).
--combobox-status-padding-xvar(--combobox-empty-padding-x)Default: var(--combobox-empty-padding-x).
--combobox-status-padding-yvar(--combobox-empty-padding-y)Default: var(--combobox-empty-padding-y).
--combobox-width16remDefault: 16rem.

Anatomy

Combobox combines an input-like control with a popup list. Keep the field parts together inside ComboboxField and keep list-related parts inside ComboboxContent so keyboard focus, selection state, and filtering stay synchronized.

Combobox
├─ ComboboxField
│  ├─ ComboboxFieldLabel
│  └─ ComboboxInputGroup
│     ├─ ComboboxInput
│     └─ ComboboxControlActions
│        ├─ ComboboxClear
│        └─ ComboboxTrigger
└─ ComboboxContent
   ├─ service slots (internal): portal, backdrop, positioner, arrow
   ├─ ComboboxStatus / ComboboxEmpty
   └─ ComboboxList
      ├─ ComboboxGroup / ComboboxCollection
      ├─ ComboboxRow (for grid lists)
      ├─ ComboboxSeparator
      └─ ComboboxItem[value]
         ├─ ComboboxItemIndicator
         └─ ComboboxItemText
<Combobox items={fruits} itemToStringLabel={(item) => item.label}>
  <ComboboxField>
    <ComboboxFieldLabel htmlFor={id}>Choose fruit</ComboboxFieldLabel>
    <ComboboxInputGroup>
      <ComboboxInput id={id} placeholder="e.g. Mango" />
      <ComboboxControlActions>
        <ComboboxClear aria-label="Clear selection" />
        <ComboboxTrigger aria-label="Open options" />
      </ComboboxControlActions>
    </ComboboxInputGroup>
  </ComboboxField>

  <ComboboxContent>
    <ComboboxEmpty>No fruits found.</ComboboxEmpty>
    <ComboboxList>
      {(item) => (
        <ComboboxItem key={item.id} value={item}>
          <ComboboxItemIndicator />
          <ComboboxItemText>{item.label}</ComboboxItemText>
        </ComboboxItem>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>
PartRole
ComboboxRoot state machine. Controls selected value, input text, popup open state, filtering, and keyboard navigation.
ComboboxFieldField wrapper for label and input area. Keep visible form-control pieces here for layout and accessibility semantics.
ComboboxInputSearch/input control. Drives filtering and exposes input events, placeholder, and focus behavior.
ComboboxControlActionsSlot for inline controls such as clear and trigger buttons.
ComboboxContentPopup surface with list/status/empty content. Also configures internal service layers and popup behavior props.
ComboboxListCollection renderer for options or grouped options. Contains ComboboxItem, ComboboxGroup, and ComboboxCollection.
ComboboxItemOne selectable option. Renders selected state and text/indicator subparts.
ComboboxChipsMultiple-selection chip container used with multiple and ComboboxValue.

In most cases, keep default popup infrastructure and style the visible parts (ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxItemText, ComboboxContent). Use ComboboxContent service-slot props (classNames, portalProps, positionerProps, backdropProps, arrowProps, withBackdrop, arrow) only when you need custom portal, layering, backdrop, or arrow behavior.

Composition

Use Combobox for root state and behavior props such as value, defaultValue, onValueChange, inputValue, onInputValueChange, open, onOpenChange, multiple, filter, filteredItems, limit, grid, virtualized, modal, openOnInputClick, autoHighlight, loopFocus, itemToStringLabel, itemToStringValue, and isItemEqualToValue.

The moduix wrapper keeps Base UI behavior available while hiding service elements from the recommended composition:

Base UI capabilitymoduix API
Root state, filtering, forms, grid modePass Base UI root props directly to Combobox.
Flat itemsPass items to Combobox and render items with a function child on ComboboxList.
Grouped itemsPass grouped items, then render ComboboxGroup, ComboboxGroupLabel, and ComboboxCollection.
Async or remote searchPass filter={null}, manage items, and announce progress with ComboboxStatus.
Externally filtered itemsPass filteredItems or combine useComboboxFilter with application state.
Virtualized listsPass virtualized, render custom rows in ComboboxList, and read useComboboxFilteredItems.
Multiple valuesPass multiple and render chips through ComboboxValue, ComboboxChips, and ComboboxChip.
Popup placementPass side, align, offsets, collision props, and container to ComboboxContent.
Portal, backdrop, positioner, arrowUse ComboboxContent props instead of composing those service slots manually.

className styles the visible popup. classNames styles the internal service slots that are hidden from the default composition. Use container, portalProps, backdropProps, positionerProps, arrowProps, withBackdrop, and arrow when you need the matching Base UI escape hatches:

<ComboboxContent
  className={styles.popup}
  withArrow
  withBackdrop
  classNames={{
    portal: styles.portal,
    backdrop: styles.backdrop,
    positioner: styles.positioner,
    arrow: styles.arrow,
  }}
/>

Every visible part accepts className and the Base UI render prop where the underlying primitive supports it, so the component can be styled with CSS Modules, plain CSS, CSS-in-JS, cascade layers, or design-system wrappers. Default icons are only fallbacks: pass children to ComboboxTrigger, ComboboxIcon, ComboboxClear, ComboboxItemIndicator, and ComboboxChipRemove to use your own icon set.

Examples

Indicator Right With Icon

Use indicator="end" when the selected indicator should be aligned to the end. Pass children to icon slots to use icons from your application.

Choose fruit
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxInputGroup,  ComboboxInput,  ComboboxControlActions,  ComboboxClear,  ComboboxTrigger,  ComboboxContent,  ComboboxList,  ComboboxItem,  ComboboxItemText,  ComboboxItemTextContent,  ComboboxItemTextIcon,  InfoIcon,  ComboboxItemTextLabel,  ComboboxItemIndicator,} from "moduix";import { useId } from "react";export function IndicatorRightComboboxDemo() {  const id = useId();  return (    <Combobox items={fruits} itemToStringLabel={(item) => item.label}>      <ComboboxField>        <ComboboxFieldLabel htmlFor={id}>Choose fruit</ComboboxFieldLabel>        <ComboboxInputGroup>          <ComboboxInput id={id} placeholder="e.g. Mango" />          <ComboboxControlActions>            <ComboboxClear aria-label="Clear selection" />            <ComboboxTrigger aria-label="Open options" />          </ComboboxControlActions>        </ComboboxInputGroup>      </ComboboxField>      <ComboboxContent>        <ComboboxList>          {(item) => (            <ComboboxItem key={item.id} value={item} indicator="end">              <ComboboxItemText>                <ComboboxItemTextContent>                  <ComboboxItemTextIcon>                    <InfoIcon />                  </ComboboxItemTextIcon>                  <ComboboxItemTextLabel>{item.label}</ComboboxItemTextLabel>                </ComboboxItemTextContent>              </ComboboxItemText>              <ComboboxItemIndicator />            </ComboboxItem>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
const fruits = [  { id: "apple", label: "Apple", value: "apple" },  { id: "banana", label: "Banana", value: "banana" },  { id: "mango", label: "Mango", value: "mango" },];

Input Inside Popup

Render ComboboxInput inside the popup when the closed field should behave like a select trigger.

Country
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxTrigger,  ComboboxValue,  ComboboxIcon,  ComboboxContent,  ComboboxInlineInputContainer,  ComboboxInput,  ComboboxEmpty,  ComboboxList,  ComboboxItem,  ComboboxItemIndicator,  ComboboxItemText,} from "moduix";import styles from "./combobox.module.css";export function InputInsidePopupComboboxDemo() {  return (    <Combobox items={countries} itemToStringLabel={(item) => item.label}>      <ComboboxField>        <ComboboxFieldLabel>Country</ComboboxFieldLabel>        <ComboboxTrigger className={styles.triggerField}>          <ComboboxValue placeholder="Select country" />          <ComboboxIcon />        </ComboboxTrigger>      </ComboboxField>      <ComboboxContent className={styles.popupWithInlineInput}>        <ComboboxInlineInputContainer>          <ComboboxInput className={styles.inlineInput} placeholder="Search country" />        </ComboboxInlineInputContainer>        <ComboboxEmpty>No countries found.</ComboboxEmpty>        <ComboboxList className={styles.listWithInlineInput}>          {(item) => (            <ComboboxItem key={item.id} value={item}>              <ComboboxItemIndicator />              <ComboboxItemText>{item.label}</ComboboxItemText>            </ComboboxItem>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
.triggerField {  display: inline-flex;  width: var(--combobox-width, 16rem);  min-height: var(--combobox-control-height, var(--size-lg));  align-items: center;  justify-content: space-between;  gap: 0.75rem;  padding: 0 0.875rem;  border: var(--border-width-sm) solid var(--combobox-border-color, var(--color-border));  border-radius: var(--combobox-radius, var(--radius-md));  background-color: var(--combobox-bg, var(--color-background));  color: var(--combobox-color, var(--color-foreground));  transition:    background-color var(--transition-default),    border-color var(--transition-default),    outline-color var(--transition-default);  &[data-popup-open] {    background-color: var(--color-muted);  }  &:focus-visible {    border-color: var(--combobox-focus-ring-color, var(--color-ring));    outline: var(--border-width-sm) solid var(--combobox-focus-ring-color, var(--color-ring));    outline-offset: calc(var(--border-width-sm) * -1);  }}.popupWithInlineInput {  --combobox-inline-input-container-height: calc(    var(--combobox-control-height, var(--size-lg)) +      var(--combobox-inline-input-container-padding-top, var(--spacing-2)) +      var(--combobox-inline-input-container-padding-bottom, var(--spacing-2)) +      var(--combobox-inline-input-divider-width, var(--border-width-sm))  );}.listWithInlineInput {  max-height: min(    calc(      var(--combobox-list-max-height, var(--combobox-popup-max-height, 24rem)) -        var(--combobox-inline-input-container-height, 0px)    ),    calc(var(--available-height) - var(--combobox-inline-input-container-height, 0px))  );  padding-top: 0.375rem;  padding-bottom: 0.5rem;  scroll-padding-top: 0.375rem;  scroll-padding-bottom: 0.5rem;}.inlineInput {  padding-inline: 0.75rem;  border: var(--border-width-sm) solid var(--combobox-border-color, var(--color-border));  border-radius: var(--radius-sm);}
const countries = [  { id: "de", label: "Germany", value: "germany" },  { id: "es", label: "Spain", value: "spain" },  { id: "fr", label: "France", value: "france" },];

Grouped

Pass grouped data to Combobox and render ComboboxCollection inside each ComboboxGroup.

Select produce
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxInputGroup,  ComboboxInput,  ComboboxControlActions,  ComboboxClear,  ComboboxTrigger,  ComboboxContent,  ComboboxEmpty,  ComboboxList,  ComboboxGroup,  ComboboxGroupLabel,  ComboboxCollection,  ComboboxItem,  ComboboxItemIndicator,  ComboboxItemText,} from "moduix";import { useId } from "react";export function GroupedComboboxDemo() {  const id = useId();  return (    <Combobox items={groupedProduce} itemToStringLabel={(item) => item.label}>      <ComboboxField>        <ComboboxFieldLabel htmlFor={id}>Select produce</ComboboxFieldLabel>        <ComboboxInputGroup>          <ComboboxInput id={id} placeholder="e.g. Spinach" />          <ComboboxControlActions>            <ComboboxClear aria-label="Clear selection" />            <ComboboxTrigger aria-label="Open groups" />          </ComboboxControlActions>        </ComboboxInputGroup>      </ComboboxField>      <ComboboxContent>        <ComboboxEmpty>No produce found.</ComboboxEmpty>        <ComboboxList>          {(group) => (            <ComboboxGroup key={group.value} items={group.items}>              <ComboboxGroupLabel>{group.value}</ComboboxGroupLabel>              <ComboboxCollection>                {(item) => (                  <ComboboxItem key={item.id} value={item}>                    <ComboboxItemIndicator />                    <ComboboxItemText>{item.label}</ComboboxItemText>                  </ComboboxItem>                )}              </ComboboxCollection>            </ComboboxGroup>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
const groupedProduce = [  {    value: "Fruits",    items: [      { id: "fruit-apple", label: "Apple", value: "apple" },      { id: "fruit-mango", label: "Mango", value: "mango" },    ],  },  {    value: "Vegetables",    items: [      { id: "veg-carrot", label: "Carrot", value: "carrot" },      { id: "veg-spinach", label: "Spinach", value: "spinach" },    ],  },];

Multiple

Use the multiple prop and render chips inside ComboboxValue. The input remains the main control, so a separate trigger icon is optional.

Select fruits
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxInputGroup,  ComboboxChips,  ComboboxValue,  ComboboxChip,  ComboboxChipText,  ComboboxChipRemove,  ComboboxChipsInput,  ComboboxContent,  ComboboxEmpty,  ComboboxList,  ComboboxItem,  ComboboxItemIndicator,  ComboboxItemText,} from "moduix";import { useId } from "react";export function MultipleComboboxDemo() {  const id = useId();  return (    <Combobox items={fruits} itemToStringLabel={(item) => item.label} multiple>      <ComboboxField>        <ComboboxFieldLabel htmlFor={id}>Select fruits</ComboboxFieldLabel>        <ComboboxInputGroup>          <ComboboxChips>            <ComboboxValue>              {(value) => (                <>                  {value.map((item) => (                    <ComboboxChip key={item.id} aria-label={item.label}>                      <ComboboxChipText>{item.label}</ComboboxChipText>                      <ComboboxChipRemove aria-label={`Remove ${item.label}`} />                    </ComboboxChip>                  ))}                  <ComboboxChipsInput                    id={id}                    placeholder={value.length === 0 ? "Select..." : ""}                  />                </>              )}            </ComboboxValue>          </ComboboxChips>        </ComboboxInputGroup>      </ComboboxField>      <ComboboxContent>        <ComboboxEmpty>No fruits found.</ComboboxEmpty>        <ComboboxList>          {(item) => (            <ComboboxItem key={item.id} value={item}>              <ComboboxItemIndicator />              <ComboboxItemText>{item.label}</ComboboxItemText>            </ComboboxItem>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
const fruits = [  { id: "apple", label: "Apple", value: "apple" },  { id: "banana", label: "Banana", value: "banana" },  { id: "mango", label: "Mango", value: "mango" },];

Disable the built-in filter with filter={null} when results are loaded from your application state or a remote API.

Assign reviewer
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxInputGroup,  ComboboxInput,  ComboboxControlActions,  ComboboxClear,  ComboboxTrigger,  ComboboxContent,  ComboboxStatus,  ComboboxEmpty,  ComboboxList,  ComboboxItem,  ComboboxItemIndicator,  ComboboxItemText,  useComboboxFilter,} from "moduix";import { useId, useMemo, useRef, useState, useTransition } from "react";export function AsyncSearchComboboxDemo() {  const id = useId();  const { contains } = useComboboxFilter();  const [searchResults, setSearchResults] = useState([]);  const [selectedValue, setSelectedValue] = useState(null);  const [searchValue, setSearchValue] = useState("");  const [isPending, startTransition] = useTransition();  const abortControllerRef = useRef(null);  const trimmedSearchValue = searchValue.trim();  const items = useMemo(() => {    if (!selectedValue || searchResults.some((user) => user.id === selectedValue.id)) {      return searchResults;    }    return [...searchResults, selectedValue];  }, [searchResults, selectedValue]);  const status = isPending    ? "Searching..."    : trimmedSearchValue === ""      ? selectedValue ? null : "Start typing to search people..."      : searchResults.length === 0        ? `No matches for "${trimmedSearchValue}".`        : null;  return (    <Combobox      items={items}      itemToStringLabel={(user) => user.name}      filter={null}      onValueChange={(nextSelectedValue) => {        setSelectedValue(nextSelectedValue);        setSearchValue("");      }}      onInputValueChange={(nextSearchValue, { reason }) => {        setSearchValue(nextSearchValue);        if (nextSearchValue === "") {          abortControllerRef.current?.abort();          setSearchResults([]);          return;        }        if (reason === "item-press") {          return;        }        const controller = new AbortController();        abortControllerRef.current?.abort();        abortControllerRef.current = controller;        startTransition(async () => {          await new Promise((resolve) => setTimeout(resolve, 250));          if (controller.signal.aborted) {            return;          }          setSearchResults(            directoryUsers.filter(              (user) =>                contains(user.name, nextSearchValue) ||                contains(user.username, nextSearchValue) ||                contains(user.email, nextSearchValue) ||                contains(user.title, nextSearchValue),            ),          );        });      }}    >      <ComboboxField>        <ComboboxFieldLabel htmlFor={id}>Assign reviewer</ComboboxFieldLabel>        <ComboboxInputGroup>          <ComboboxInput id={id} placeholder="e.g. Michael" />          <ComboboxControlActions>            <ComboboxClear aria-label="Clear selection" />            <ComboboxTrigger aria-label="Open options" />          </ComboboxControlActions>        </ComboboxInputGroup>      </ComboboxField>      <ComboboxContent>        <ComboboxStatus>{status}</ComboboxStatus>        <ComboboxEmpty>          {trimmedSearchValue !== "" && !isPending && searchResults.length === 0            ? "Try a different search term."            : null}        </ComboboxEmpty>        <ComboboxList>          {(user) => (            <ComboboxItem key={user.id} value={user}>              <ComboboxItemIndicator />              <ComboboxItemText>{user.name}</ComboboxItemText>            </ComboboxItem>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
const directoryUsers = [  {    id: "leslie-alexander",    name: "Leslie Alexander",    username: "leslie",    email: "leslie.alexander@example.com",    title: "Product Manager",  },  {    id: "michael-foster",    name: "Michael Foster",    username: "michael",    email: "michael.foster@example.com",    title: "Frontend Engineer",  },];

Custom Styles

Use ComboboxContent props for popup infrastructure. Portal, Positioner, Backdrop, and Arrow are rendered internally, so application code only configures behavior and service-slot classes.

Choose fruit
import {  Combobox,  ComboboxField,  ComboboxFieldLabel,  ComboboxInputGroup,  ComboboxInput,  ComboboxControlActions,  ComboboxClear,  ComboboxTrigger,  ComboboxContent,  ComboboxEmpty,  ComboboxList,  ComboboxItem,  ComboboxItemIndicator,  ComboboxItemText,} from "moduix";import { useId } from "react";import styles from "./combobox.module.css";export function CustomStylesComboboxDemo() {  const id = useId();  return (    <Combobox items={fruits} itemToStringLabel={(item) => item.label}>      <ComboboxField>        <ComboboxFieldLabel htmlFor={id}>Choose fruit</ComboboxFieldLabel>        <ComboboxInputGroup>          <ComboboxInput id={id} placeholder="e.g. Mango" />          <ComboboxControlActions>            <ComboboxClear aria-label="Clear selection" />            <ComboboxTrigger aria-label="Open options" />          </ComboboxControlActions>        </ComboboxInputGroup>      </ComboboxField>      <ComboboxContent        className={styles.customPopup}        sideOffset={8}        withArrow        withBackdrop        classNames={{          portal: styles.portal,          backdrop: styles.backdrop,          positioner: styles.positioner,          arrow: styles.arrow,        }}      >        <ComboboxEmpty>No fruits found.</ComboboxEmpty>        <ComboboxList>          {(item) => (            <ComboboxItem key={item.id} value={item}>              <ComboboxItemIndicator />              <ComboboxItemText>{item.label}</ComboboxItemText>            </ComboboxItem>          )}        </ComboboxList>      </ComboboxContent>    </Combobox>  );}
.customPopup {  --combobox-popup-bg: var(--color-background);  --combobox-popup-border-color: var(--color-ring);  --combobox-shadow: var(--shadow-xl);}.portal {  isolation: isolate;}.positioner {  z-index: var(--z-popup);}.backdrop {  --combobox-backdrop-bg: var(--color-overlay);  --combobox-backdrop-blur: 2px;}.arrow {  --combobox-arrow-stroke-color: var(--color-border);}
const fruits = [  { id: "apple", label: "Apple", value: "apple" },  { id: "banana", label: "Banana", value: "banana" },  { id: "mango", label: "Mango", value: "mango" },];

On this page