Components

Select

A React select with a Liquid Glass trigger for single or multiple values, grouped options, and five sizes.

Select renders the label, trigger, glass surface, selected value, chevron, portal, glass popup, and options internally. Options are passed through the items array, so the same data drives both the trigger value and the popup list. Items can be flat or grouped, and multiple switches the value to an array.

Use label and description for visible field text. When no visible label is rendered, give the trigger an accessible name with aria-label or aria-labelledby. The selected value is submitted through a hidden native input when name is set.

Basic example

The example shows a labelled field with a description, a placeholder, and a disabled option. The first option is selected through defaultValue.

Fruit
Choose the fruit for the next delivery.

Multiple selection

Set multiple to select more than one option. The value becomes an array, the popup stays open between selections, and the list exposes aria-multiselectable. Use renderValue when the trigger should summarize the selection instead of listing every label; it overrides placeholder, so return the empty-state text yourself.

Languages
The popup stays open while you toggle options.

Grouped options

Pass groups as { label, items } entries in items. Each group renders a heading and is announced as a labelled group, while the trigger still resolves labels from the flattened options.

Produce
Options are grouped by category.

Object values

Value is not limited to strings. Pass objects as item values and use itemToStringValue for form submission and isItemEqualToValue when equal records are not the same reference. renderValue can show the full object inside the trigger.

Shipping method
Object values keep the full record in state.

Sizes

Supports xs / sm / md / lg / xl, defaulting to md. Trigger height, padding, text, and the chevron scale together.

xs
sm
md
lg
xl

Controlled usage

value and onValueChange control the selection. Set the controlled value to null to clear it; the component does not render its own clear control, so the example keeps a clear action next to the field.

Plan
Switch plans at any time.

Selected plan: team

Form usage

Set name to submit the selected value through a hidden native input. required participates in native form validation, and form links the field to a form elsewhere in the document.

Plan
The selected value is submitted with the form.

No value submitted.

Accessibility

Select follows the Base UI select pattern: the trigger is a combobox with aria-haspopup="listbox", aria-expanded, and aria-controls; the inner list is the listbox, and each option is an option. ArrowDown and ArrowUp move through options, typeahead matches labels, and Enter selects the highlighted option. Escape closes the popup and returns focus to the trigger.

With multiple, the list reports aria-multiselectable="true", each option exposes aria-selected, and activating an option toggles it without closing the popup. Grouped options render inside a group labelled by their heading.

The visible label names the trigger and description is linked with aria-describedby. Without a visible label, use aria-label or aria-labelledby. Disabled options remain announced but cannot be selected. Set required when a value must be chosen, and see the accessibility guide for shared form patterns.

API reference

Select

PropTypeDefaultDescription
itemsreadonly (SelectItemProps | SelectItemGroupProps)[]—Options rendered by the select, optionally grouped. Required.
labelReactNode—Visible label associated with the trigger.
descriptionReactNode—Supporting text under the trigger, linked with aria-describedby.
placeholderReactNode—Text shown when no value is selected.
renderValue(value) => ReactNode—Formats the trigger value. Receives Value | null, or readonly Value[] when multiple. Overrides placeholder.
multiplebooleanfalseAllows selecting several options; value, defaultValue, and onValueChange then use arrays.
valueValue | null—Controlled value. Use readonly Value[] | null when multiple.
defaultValueValue | null—Initial value when uncontrolled. Use readonly Value[] | null when multiple.
onValueChange(value, details) => void—Called when the selected value changes. Receives Value | null, or Value[] when multiple.
openboolean—Controlled popup open state.
defaultOpenbooleanfalseInitial popup open state when uncontrolled.
onOpenChange(open: boolean, details) => void—Called when the popup opens or closes.
onOpenChangeComplete(open: boolean) => void—Called after the open or close animation finishes.
modalbooleantrueLocks outside interaction while the popup is open.
readOnlybooleanfalsePrevents changing the selected value.
disabledbooleanfalseDisables the trigger and options.
highlightItemOnHoverbooleantrueHighlights options on pointer move. Set to false to style :hover separately from data-highlighted.
itemToStringLabel(itemValue: Value) => string—Converts an object value to the text shown in the trigger.
itemToStringValue(itemValue: Value) => string—Converts an object value to the string submitted with the form.
isItemEqualToValue(itemValue: Value, value: Value) => booleanObject.isCustom equality used to match object values that are not the same reference.
namestring—Form field name for the hidden input. In multiple mode one input is submitted per selected value.
formstring—Form that owns the hidden input.
autoCompletestring—Autofill hint forwarded to the hidden input.
requiredbooleanfalseRequires a value before form submission.
idstring—Base id for the select parts.
actionsRefRefObject<Select.Root.Actions | null>—Imperative close and unmount actions.
inputRefRef<HTMLInputElement>—Ref to the hidden input element.
aria-labelstring—Accessible name for the trigger when no visible label is rendered.
aria-labelledbystring—Names the trigger from another element.
aria-describedbystring—Additional description IDs applied to the trigger.
size"xs" | "sm" | "md" | "lg" | "xl""md"Controls trigger height, padding, text, and icon size.
side"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"Preferred side of the trigger.
align"start" | "center" | "end""center"Alignment relative to the trigger.
sideOffsetnumber | OffsetFunction6Distance between the trigger and popup in pixels.
alignOffsetnumber | OffsetFunction0Offset along the alignment axis in pixels.
collisionPaddingPadding5px per sideSpace kept between the popup and the collision boundary.
alignItemWithTriggerbooleanfalseOverlaps the popup so the selected item aligns with the trigger.
classNamestring—Tailwind classes merged into the field root.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ root?: ClassValue; label?: ClassValue; trigger?: ClassValue; glass?: ClassValue; overlay?: ClassValue; value?: ClassValue; icon?: ClassValue; description?: ClassValue; positioner?: ClassValue; popup?: ClassValue; popupGlass?: ClassValue; popupOverlay?: ClassValue; list?: ClassValue; group?: ClassValue; groupLabel?: ClassValue; item?: ClassValue; itemIndicator?: ClassValue; itemText?: ClassValue }—Tailwind classes for internal slots, keyed by data-slot.

SelectItemProps

PropTypeDefaultDescription
valueany—Value submitted and passed to onValueChange. Any type is accepted. Required.
labelReactNode—Visible content inside the option and trigger. Required.
textValuestring—Plain-text label used for keyboard typeahead. Defaults to a string label.
disabledbooleanfalseDisables the option.

SelectItemGroupProps

PropTypeDefaultDescription
labelReactNode—Visible heading rendered above the group. Required.
itemsreadonly SelectItemProps[]—Options in the group. Required.

ui keys map to the data-slot values listed below. All other Base UI Select.Root props are forwarded unchanged.

AttributeDescription
data-openPresent on the popup when the select is open.
data-closedPresent on the popup while it is closed or closing.
data-placeholderPresent on the value when no option is selected.
data-selectedPresent on the selected option and its indicator.
data-highlightedPresent on the keyboard- or pointer-highlighted option.
data-disabledPresent when the select or an option is disabled.
data-readonlyPresent when the select is read-only.
data-popup-openPresent on the trigger while the popup is open.
data-sideIndicates the resolved side of the popup.
data-alignIndicates the resolved alignment (start, center, or end).
data-starting-stylePresent when the popup begins animating in.
data-ending-stylePresent when the popup is animating out.
aria-multiselectabletrue on the list when multiple is set.
data-slotMarks internal parts (root, label, trigger, glass, overlay, value, icon, description, positioner, popup, popup-glass, popup-overlay, list, group, group-label, item, item-indicator, item-text).

Theming

The component uses the shared glass material. Tune shared --ui-glass-* tokens on a parent, use className for layout, and use ui for internal slots keyed by data-slot. Component-specific values live in src/theme/select.ts. See the theming guide for token layers and presets.

  • Input — For free-form text fields next to a select.
  • Menu — For actions rather than a persistent selected value.
  • Radio Group — When every option should stay visible.