Select
Pick one value from a short list. A pill trigger opens the brand popover menu; keyboard, typeahead and focus return come from Radix. Source: src/components/ui/select.tsx.
Default
Label it with htmlFor pointing at the trigger's id. Controlled with value and onValueChange; the note below follows the choice.
The default for most agents
Variants
Outline for a sort or setting on the canvas; tint for a toolbar, next to tint Buttons. Both lift to tint on hover and while open.
Sizes
Two heights, on the Button scale, so a select lines up with the buttons beside it.
Groups, placeholder and validation
Group long lists under labels. A placeholder means nothing is chosen yet. Submit without choosing to see the error.
Known gap: the trigger has no aria-invalid style, unlike Input. The attribute is still announced to screen readers, but the error is carried by the message alone. There is no Field primitive to wire the two.
Disabled
Disable the whole select on the root, or single items with disabled on SelectItem.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| Select | Radix Select.Root props | — | value / defaultValue, onValueChange, open / onOpenChange, disabled, required, name (for native form posts). |
| SelectTrigger variant | "outline" | "tint" | "outline" | outline is the Button outline look; tint is the toolbar pill. Mirrored as data-variant. |
| SelectTrigger size | "sm" | "default" | "default" | 32px or 36px, matching Button sm and default. Set via data-size, not cva. |
| SelectValue placeholder | ReactNode | — | Shown, on the third text tier, until a value is chosen. |
| SelectContent | Radix Select.Content props | position="popper", sideOffset={4} | The menu: 14px corners, glass shadow, at least as wide as the trigger. Enter 140ms, exit 90ms. |
| SelectItem | Radix Select.Item props | — | value is required; disabled dims it and skips it in keyboard navigation. The selected item shows an accent check. |
| SelectGroup / SelectLabel / SelectSeparator | Radix props | — | Group related items under a non-selectable label; a hairline separates groups. |
Best practices
When to use
- One choice from roughly 3 to 15 options.
- Two or three options that should all stay visible: a Toggle group.
- A long or searchable list needs a combobox (Command in a Popover); there is no Combobox primitive yet.
- Choosing an action, not a value: a Dropdown menu.
Behavior
- Give the trigger a fixed width (w-48) when values differ in length, so the layout doesn't jump on change.
- Default to the most common value rather than a placeholder when one is safe.
- The menu opens below as a popover; it never covers the trigger.
Accessibility
- Label it: Label htmlFor the trigger id, or aria-label on the trigger in a toolbar.
- Arrow keys move, typing jumps to a match, Enter selects, Escape closes and returns focus.
- Link helper and error text with aria-describedby on the trigger.
Content
- Options are short and parallel: Most recent, Oldest, Name. Explain the effect below the field, not in the option.