Dropdown for choosing one value from a short list, with built-in keyboard, focus, and form-field composition.
Description
Select is a compound primitive built on top of Radix's Select. It composes Select, SelectTrigger, SelectContent, and SelectItem (with optional SelectValue, SelectGroup, SelectLabel, SelectSeparator, and SelectShell) into a fully-managed listbox popover. The trigger auto-wraps in SelectShell when standalone, and skips that wrapper when it detects an InputGroup or an explicit SelectShell ancestor so a single shared chrome can host multiple inputs.
Reach for it for short, mutually-exclusive value lists: country, currency, role, status, billing plan. It works as both a labelled form control (wrapped in Field) and a compact toolbar control. Size and validation state cascade from Field via context, so you set them in one place.
Don't use it for long searchable lists; use Combobox. For flat one-of-many choices where surfacing every option saves a click, use RadioGroup. For booleans, use Switch. For free-form input, use Input. The multi-select pattern shown below works fine for handfuls of values, but past ~10 options prefer a dedicated multi-select combobox.
Installation
Anatomy
SelectTrigger wraps itself in SelectShell automatically when used standalone. When it detects an InputShell (from InputGroup) or an explicit SelectShell ancestor, it skips its own chrome so the surrounding container owns the border, focus ring, and validation styles.
Usage
Examples
Variants
variant="default" is a full-width trigger with placeholder text, the form-field shape. variant="compact" shrinks the trigger to fit its content, ideal for toolbar pickers and icon-only controls; pair it with aria-label on SelectTrigger.
Sizes
Three sizes (xs, sm, md) match the rest of the form scale. sm is the default; pick md for hero forms and xs for dense settings tables.
States
Default, disabled, invalid, and loading. invalid tints the border and outline with error tokens; loading swaps the caret for a spinner and tints the chrome with info tokens. All four cascade from the surrounding Field if one is present.
With Icon
Render a leading element inside SelectTrigger to mirror the selected item's icon. Pass the placeholder icon when no value is set, and swap it for the active item's icon once a value is chosen. Drop a trailing element (e.g. a Badge) between SelectValue and the auto-rendered caret to surface a secondary label like a ticker or shortcut.
Controlled
Pass value and onValueChange to manage the state yourself. Use uncontrolled defaultValue for simple forms; reach for controlled when other UI needs to react to the selection.
Inside an Input Group
Drop a Select variant="compact" next to an Input inside InputGroup and the trigger detects the surrounding InputShell, skips its own chrome, and lets the group own the single shared border and focus ring.
Multi-select
Multi-select isn't a built-in mode of Radix's Select, but the primitive composes well: intercept onValueChange to toggle an array, intercept onOpenChange to keep the popover open after a click, and render the selection as Chips inside the trigger.
Accessibility
SelectTrigger and SelectContent inherit Radix's listbox keyboard model. The trigger is a single tab stop; arrow keys, type-ahead, and Home / End operate inside the open listbox.
ARIA notes:
- Radix wires
role="combobox"on the trigger,role="listbox"on the content, androle="option"on items, plusaria-expanded,aria-controls,aria-activedescendant, andaria-selected. - The component forwards
invalidtoaria-invalidon the trigger (and reads fromFieldcontext when unset).loadingsetsaria-busy="true". - For
variant="compact"(icon-only trigger), always passaria-labelonSelectTriggerso the control announces its purpose. - For deeper focus, portal, and animation behavior, see Radix Select.
Styling
Tailwind override: pass className to merge Tailwind classes on the trigger or any sub-part (merged via cn()):
Data slots and attributes: the component sets these for CSS targeting:
data-slot="select"on the root,data-slot="select-shell"on the chrome wrapper,data-slot="select-trigger"on the trigger,data-slot="select-content"on the popover,data-slot="select-value"on the value,data-slot="select-group",data-slot="select-label",data-slot="select-item",data-slot="select-separator".data-size="<size>",data-variant="<variant>",data-invalid,data-disabled,data-loadingon both shell and trigger.- Radix-managed:
data-state="open" | "closed"on trigger and content,data-sideon the content,data-highlighted,data-state="checked" | "unchecked", anddata-disabledon each item.
Target a state in CSS:
Related Components
- Combobox: searchable or async variant; use when the list is long or users need to filter.
- RadioGroup: flat one-of-many selection always visible inline; use when there are 2ā4 options and surfacing them saves a click.
- NativeSelect: native
<select>wrapper; reach for it when the form must work without JavaScript or you want the OS picker on mobile. - Input: free-text entry; use when values aren't constrained to a list.
- InputGroup: the chrome wrapper used in the embedded-select pattern.
API Reference
Select
Wraps Radix's Select.Root and provides the SelectContext that sub-parts read for size, variant, and validation state. Extends React.ComponentProps<typeof SelectPrimitive.Root>, so all Radix root props (name, required, dir, etc.) pass through.
Props
Variants
SelectTrigger
Wraps Radix's Select.Trigger and auto-wraps itself in SelectShell when it doesn't detect a parent shell. Extends Radix Select.Trigger props (asChild, aria-label, aria-labelledby, etc.).
Props
SelectValue
Wraps Radix's Select.Value. Renders the selected item's label, or the placeholder when nothing is selected.
Props
SelectShell
Manual chrome wrapper for hand-rolled trigger layouts (e.g. composing a select with another input). Extends React.ComponentProps<"div">. Reads size, variant, and state from SelectContext if present; falls back to sm / default otherwise.
Props
Variants
SelectContent
Wraps Radix's Select.Content rendered inside Select.Portal. Extends React.ComponentProps<typeof SelectPrimitive.Content>.
Props
SelectGroup
Wraps Radix's Select.Group. Use it to group related items, optionally with a SelectLabel header.
SelectLabel
Wraps Radix's Select.Label. Sized by the surrounding SelectContext.
Props
SelectItem
Wraps Radix's Select.Item. The first child (if more than one is passed) becomes the leading element; the remaining children become the item text. A trailing check mark is rendered from Select.ItemIndicator when the item is selected.
Props
SelectSeparator
Wraps Radix's Select.Separator. A thin bg-weak divider for splitting groups inside the content.