Select
Displays a list of options for the user to pick from, opened from a button.
Installation
Install the following dependencies:
Set up the token layer and the StyleX compiler, if you have not already. See
Installation, and paste states.ts and state-styles.ts
from the same page. states.ts holds the popup surface, the item highlight and
its nested corner, the trigger's field surface, its focus and invalid rings, and
the dim a disabled trigger or item takes. state-styles.ts is how the trigger
and the items read their Base UI state. Include interop.css for icon sizing in the trigger, items and scroll
arrows.
Paste field-state.ts from the same installation page.
Copy and paste the following code into your project.
Update the import paths to match your project setup.
Usage
Pass the options through items so SelectValue can show the chosen item's
label rather than its value. An entry whose value is null is the
placeholder. Without items, pass placeholder to SelectValue instead.
Give the trigger an aria-label or a Label with htmlFor pointing at its
id. There is no Field to name it.
Wrap items in a SelectGroup, even without a label. The group carries the
popup's inset, so a bare item sits flush against the edge.
The trigger sizes itself to its content. Give it a width through xstyle to
stop it changing size when the value does. Right-to-left layout is not covered
here.
Composition
Align item with trigger
Set alignItemWithTrigger on SelectContent. When true, the popup opens
over the trigger with the selected item on top of it, the way a native select
does. When false, it opens below the trigger like a menu.
The overlap is skipped on touch, and when there is no room for it. The popup then opens from the trigger's edge and slides in from that side instead of fading in place.
Groups
Use SelectGroup, SelectLabel and SelectSeparator to organise items.
Scrollable
A list taller than the space below the trigger scrolls. Hovering the arrow at either end scrolls it. On touch, the arrows do not render.
Size
Use the size prop on SelectTrigger.
Disabled
Set disabled on Field to disable its control and dim its label, or on a bare control directly.
Invalid
Set invalid on Field to style its control, or aria-invalid on a bare control. The examples show both.
API reference
Pass render to replace a part's element or compose it with another component.
Every part wraps the Base UI Select part of the same name and accepts its
props. See the Base UI reference
for the full list. Each styled part also takes xstyle and className.
SelectTrigger and SelectItem take style too. It is real inline CSS, merged
over whatever their Base UI state resolves to.
Select
| Prop | Type | Default |
|---|---|---|
items | { label: ReactNode; value: Value }[] | Record<string, …> | — |
value | Value | null | — |
defaultValue | Value | null | — |
onValueChange | (value: Value | null) => void | — |
multiple | boolean | false |
name | string | — |
required | boolean | false |
disabled | boolean | false |
readOnly | boolean | false |
name renders a hidden input, so the value travels with a form.
SelectTrigger
| Prop | Type | Default |
|---|---|---|
size | "default" | "sm" | "default" |
icon | ReactNode | chevron |
aria-invalid | boolean | — |
disabled | boolean | — |
style | CSSProperties | — |
Text inside the trigger clips with an ellipsis once it outgrows the width the trigger was given.
icon replaces the chevron after the value. Pass null to remove it.
SelectValue
| Prop | Type | Default |
|---|---|---|
placeholder | ReactNode | — |
children | ReactNode | (value) => ReactNode | — |
A function child formats the selected value. It replaces the label items
would otherwise provide.
SelectContent
| Prop | Type | Default |
|---|---|---|
side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
sideOffset | number | 4 |
align | "start" | "center" | "end" | "center" |
alignOffset | number | 0 |
alignItemWithTrigger | boolean | true |
The popup is the trigger's width and never narrower than 9rem. Its height is
capped at the space available, and the list inside scrolls.
SelectItem
| Prop | Type | Default |
|---|---|---|
value | Value | — |
disabled | boolean | false |
label | string | — |
style | CSSProperties | — |
label is the text typed letters match against. It defaults to the item's
text content.
selectStyles
The style maps behind the component. Spread them onto any element.
| Key | What it is |
|---|---|
trigger | The trigger's frame and type, ring, surface and hover |
triggerPlaceholder | The muted text while nothing is selected |
triggerDisabled | The dimmed, inert state every control shares |
invalid | The destructive border and ring |
sizes | default and sm heights |
content | The popup: the shared surface and the box over it |
item | An item's row, type and indicator inset |
itemHighlighted | The highlight fill and inset ring |
itemDisabled | The same dim, on one option |
label | A group label |
separator | The hairline between groups |
trigger is the part's own box, states.focusRing, states.fieldSurface and
the fill with its hover, in merge order. content merges states.popupSurface
(the fill and hairline ring) under the popup's own box, which sets
--carbon-popup-radius. item merges states.popupItem, so a row's corner is
that radius minus the popup's inset. itemHighlighted is
states.itemHighlight.
invalid is states.invalidRing and both disabled keys are states.disabled.
Set a trigger border of your own through --carbon-border-color rather than
borderColor. A flat borderColor replaces the property with all of its conditions, so the ring loses its focus colour.
Data attributes
| Attribute | On | Effect |
|---|---|---|
data-slot="select-trigger" | the trigger | Targets the trigger from a parent |
data-size | the trigger | Reflects the size prop |
data-placeholder | the trigger | Base UI writes it while nothing is selected |
data-popup-open, data-disabled | the trigger | Base UI writes them |
data-slot="select-content" | the popup | Popup surface |
data-side, data-align | the popup | Base UI writes them. data-side picks the slide; "none" means overlapped |
data-starting-style, data-ending-style | the popup | Base UI writes them while the popup animates in or out |
data-slot="select-item" | an item | Targets items from a parent |
data-highlighted, data-selected | an item | Base UI writes them |
data-disabled | an item | Base UI writes it |