Select

Picks one option from a list in a popup.

Examples

Activity source

A choice the form needs

Pick the language replies are written in.

A hint under the trigger

Replies use this unless the customer writes in another.

The platform's picker

Sizes

  • defaultdefault

  • sm

  • lg

States

  • rest

  • hover

  • focus-visible

  • placeholder

  • invalid

  • disabled

Usage

When to use it
Use
One choice from 5 to about 30 known options.
Use
Options of different kinds share names: pass kind.
Use
A form used mostly on phones or generated from a schema: native.
Not for
Two to six options fit on screen. Use Radio group.
Not for
There are more than 30 options, or the person types the name. Use Combobox.
Not for
The choice switches a view on this page. Use Segmented control.

Rules

9 rules
Must
Pass the options as items on Select as well as rendering SelectItems, so the trigger shows the label before the popup opens. Because Base UI finds the label from items, so without it the trigger shows the raw value "agent:triage" until the popup opens.
Must
Label the trigger with Field and FieldLabel, or aria-labelledby; its text is the value, not the question. Because the trigger text is the current value, so a screen reader hears "English, combobox" with no question.
Must
Use SelectValue placeholder to say what to pick; never add a fake first option like None. Because a None option counts as a real choice, so a required field passes empty and the list gains a fake row.
Must
Pass kind on options that share a name; never mark kinds with a colored dot. Because a dot is hue alone, which vanishes in forced colors and for color-blind readers; kind adds the word.
Should
Use native for forms used mostly on phones or generated from a schema.
Must
Use Combobox past 30 options; the popup caps at 320px, about nine items. Because the popup caps at 320px, about nine items, and Select has no search to reach the rest.
Must
Pass only options known up front; options that load belong in Combobox. Because Select has no loading or empty state, so options that arrive late leave a popup that opens blank.
Never
Render a Select with no options; show why in its place. Because an empty popup opens to nothing and gives no reason or way forward.
Should
Set aria-invalid on the trigger and show the error in the Field.

Accessibility

6 checks
The trigger is a combobox with aria-expanded, aria-haspopup="listbox" and aria-invalid.
The popup is a listbox; each item is an option with aria-selected.
Enter, Space or ArrowDown on the trigger opens it with the chosen item highlighted.
Letters jump to the next item starting with them, open or closed.
Enter picks and returns focus to the trigger; Escape closes without a change.
The kind label is text after the option name, so it reads without colour.

Edge cases

9 edge cases
Long label
Value and item text truncate with an ellipsis; the popup is at most --popover wide.
40 options at the viewport edge
The list scrolls inside a 320px popup and flips above the trigger near the bottom.
Right to left
The check and the caret sit at the end, the left in RTL.
Zoom 200%
Height is max(--control, 1lh + 12px) and grows with the text.
Invalid with no value
The edge takes the fail line; the Field shows the error.
items=[]
Opens an empty popup. Do not render it.
Inside a Dialog
The popup portals above the dialog; Escape closes only the popup.
Native on touch
Opens the platform picker; text is 16px so iOS does not zoom.
Typing "sp" fast while closed
Typeahead jumps to Spanish without opening.

API

9 props
Select items
{value, label, kind?, disabled?}[] | Record
Select value / defaultValue / onValueChange
string | null
Select native
boolean default false
Select size, placeholder
ControlSize; string default 'default'
SelectTrigger size
'sm' | 'default' | 'lg' default 'default'
SelectTrigger aria-invalid
boolean default false
SelectValue placeholder
ReactNode
SelectItem kind
'agent' | 'key' | 'run' | 'deploy' | 'model' default from items
SelectContent side, align, sideOffset
Base UI default 'bottom', 'start', 4

Tokens

23 tokens
--line-control
oklch(var(--ink-c) / 0.16)
--control
32px
--control-sm
28px
--radius-inner
4px
--radius-soft
6px
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--accent-wash
var(--surface-selected)
--accent-mark
var(--ink)
--surface-pressed
oklch(var(--ink-c) / 0.08)
--text-disabled
oklch(var(--ink-c) / 0.40)
--surface-page
var(--paper)
--focus-halo
0 0 0 3px oklch(var(--ink-c) / 0.08)
--elev-overlay
0 0 0 1px var(--line-frame), 0 12px 24px -12px oklch(var(--text-on-field-c) / var(--shadow-alpha))
--dur-base
160ms
--dur-fast
120ms
--ease-step
cubic-bezier(0.2, 0, 0, 1)
--surface-hover
oklch(var(--ink-c) / 0.05)
--type-label
400 14px/16px var(--font-sans)
--accent-tint
var(--surface-selected)
--accent-label
var(--text-1)
--fail-line
oklch(60% 0.236 26)
--popup-max
320px
--popover
288px

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.