Experience System
Components

Combobox

Filterable selection input with single and multiple selection patterns.

Installation

The component is exported from @by/experience-system. Add the package with your package manager:

pnpm add @by/experience-system

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.

Composition

Simple

A single-line input and a flat list.

Combobox
├── ComboboxInput
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        ├── ComboboxItem
        └── ComboboxItem

ComboboxTrigger is rendered inside ComboboxInput by default, and ComboboxClear is optional via showClear.

With chips

Multi-select with multiple, chips, and a chips input.

Combobox
├── ComboboxChips
│   ├── ComboboxValue
│   │   └── ComboboxChip
│   └── ComboboxChipsInput
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        ├── ComboboxItem
        └── ComboboxItem

With groups and collection

Nested items per group using ComboboxCollection inside each ComboboxGroup, with separators between groups.

Combobox
├── ComboboxInput
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        ├── ComboboxGroup
        │   ├── ComboboxLabel
        │   └── ComboboxCollection
        │       ├── ComboboxItem
        │       └── ComboboxItem
        ├── ComboboxSeparator
        └── ComboboxGroup
            ├── ComboboxLabel
            └── ComboboxCollection
                ├── ComboboxItem
                └── ComboboxItem

Usage

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@by/experience-system';

Combobox is a client component ('use client' in the package). Use it inside a Client Component when using the Next.js App Router.

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

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

Examples

Basic

Single-select combobox with filterable options.

Multiple

Use multiple with chips to select more than one value.

Clear button

Set showClear on ComboboxInput to render a clear action.

Groups

Use ComboboxGroup, ComboboxLabel, and ComboboxCollection for grouped options.

Custom items

Provide object items and map display/submission values with itemToStringValue.

API Reference

The API mirrors Base UI Combobox, with Experience System styling and field-surface extensions (size, appearance, showTrigger, showClear, and read-only behavior).

Combobox

Maps to Base UI Combobox.Root.

PropTypeDefault
itemsValue[] | Group[]
value / defaultValueValue | Value[] | null
onValueChange(value, eventDetails) => void
inputValue / defaultInputValuestring | number | string[]
onInputValueChange(inputValue, eventDetails) => void
open / defaultOpenbooleanfalse (defaultOpen)
onOpenChange(open, eventDetails) => void
multiplebooleanfalse
autoHighlightbooleanfalse
highlightItemOnHoverbooleantrue
disabledbooleanfalse
readOnlybooleanfalse (Experience System passthrough)
requiredbooleanfalse
name / form / idstring
filterComboboxFilter | null
itemToStringLabel / itemToStringValue(item) => stringauto for { label, value }
classNamestring
Data attributeValues
data-slotcombobox
data-readonlypresent when readOnly is true

ComboboxInput

Maps to Base UI Combobox.Input, rendered in an InputGroup surface.

PropTypeDefault
showTriggerbooleantrue
showClearbooleanfalse
sizesm | md | lgmd
appearancedefault | filleddefault
aria-invalidboolean
placeholderstring
disabledbooleanfalse
Data attributeValues
data-slotcombobox-input (Base UI input element)
data-popup-openpresent when popup is open
data-list-emptypresent when list has no items
data-disabledpresent when disabled
data-readonlypresent when read-only
data-invalidpresent when invalid (Field.Root integration)

ComboboxContent

Combines Base UI Combobox.Positioner + Combobox.Popup.

PropTypeDefault
side'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start''bottom'
align'start' | 'center' | 'end''start'
sideOffsetnumber4
alignOffsetnumber0
anchorElement | VirtualElement | Ref | nulltrigger/chips anchor
Data attributeValues
data-slotcombobox-content
data-sidepopup side
data-alignpopup alignment
data-open / data-closedpopup state
data-emptypresent when list is empty
CSS variableDescription
--anchor-heightAnchor height
--anchor-widthAnchor width
--available-heightAvailable viewport height
--available-widthAvailable viewport width
--transform-originPopup transform origin

ComboboxList

Maps to Base UI Combobox.List.

PropTypeDefault
childrenReactNode | (item, index) => ReactNode
Data attributeValues
data-slotcombobox-list

ComboboxItem

Maps to Base UI Combobox.Item.

PropTypeDefault
valueValuenull
disabledbooleanfalse
indexnumberauto
onClick(event) => void
childrenReactNode
Data attributeValues
data-slotcombobox-item
data-selectedpresent when selected
data-highlightedpresent when highlighted
data-disabledpresent when disabled

ComboboxGroup

Maps to Base UI Combobox.Group.

PropTypeDefault
itemsValue[]
childrenReactNode
Data attributeValues
data-slotcombobox-group

ComboboxLabel

Maps to Base UI Combobox.GroupLabel.

PropTypeDefault
childrenReactNode
Data attributeValues
data-slotcombobox-label

ComboboxCollection

Maps to Base UI Combobox.Collection.

PropTypeDefault
children(item, index) => ReactNode
Data attributeValues
data-slotcombobox-collection

ComboboxEmpty

Maps to Base UI Combobox.Empty.

PropTypeDefault
childrenReactNode
Data attributeValues
data-slotcombobox-empty

ComboboxSeparator

Maps to Base UI Combobox.Separator.

PropTypeDefault
orientation'horizontal' | 'vertical''horizontal'
Data attributeValues
data-slotcombobox-separator

ComboboxChips

Maps to Base UI Combobox.Chips with Experience System field-surface options.

PropTypeDefault
showTriggerbooleantrue
showClearbooleanfalse
sizesm | md | lgmd
appearancedefault | filleddefault
aria-invalidboolean
Data attributeValues
data-slotcombobox-chips
data-readonlypresent when read-only
data-disabledpresent when disabled

ComboboxChip

Maps to Base UI Combobox.Chip with optional remove action.

PropTypeDefault
showRemovebooleantrue
removeAriaLabelstringinferred from chip label
childrenReactNode
Data attributeValues
data-slotcombobox-chip

ComboboxChipsInput

Maps to Base UI Combobox.Input for multiple mode.

PropTypeDefault
fieldSizesm | md | lginherits chips size
placeholderstring
disabledbooleanfalse
readOnlybooleaninherited from root
Data attributeValues
data-slotcombobox-chip-input

ComboboxTrigger

Maps to Base UI Combobox.Trigger.

PropTypeDefault
aria-labelstring'Open options' (Experience System)
disabledbooleanfalse
Data attributeValues
data-slotcombobox-trigger
data-popup-openpresent when popup is open
data-placeholderpresent when no value is selected

ComboboxValue

Maps to Base UI Combobox.Value.

PropTypeDefault
placeholderReactNode
childrenReactNode | (selectedValue) => ReactNode
Data attributeValues
data-slotcombobox-value

Accessibility

Provide an accessible name for the form control (ComboboxInput with label/htmlFor, or aria-label when needed), and keep empty/status content mounted so assistive technologies announce filtering updates correctly. Group labels (ComboboxLabel) and chips remove actions should use clear text labels.

Keyboard interactions

KeyDescription
ArrowDown / ArrowUpMove highlight through items; can open and navigate the popup.
EnterSelects the highlighted item.
EscCloses the popup.
TabMoves focus according to normal tab order.
BackspaceIn multiple mode, chip removal behavior is available via chip remove controls.

See Base UI Combobox accessibility guidance and shadcn/ui Combobox composition examples.

Source in the repo: packages/experience-system/src/components/Combobox/Combobox.tsx. Agent-oriented contracts: packages/experience-system/src/components/Combobox/Combobox.instructions.md.