Select
Displays a list of options for the user to pick from—triggered by a button.
When to use
Use Select for a small, finite list of options (~2-15) where the user picks exactly one and search/filtering is unnecessary. For larger lists, async data, or any single-select where typing to filter is helpful, use Combobox. For picking multiple options, use Multi Select.
When a Select is rendered inside Field.Item, wrap Select.Root in Field.Control. Field.Control flows the generated id, name, aria-describedby, aria-errormessage, and aria-invalid through to the focusable Select.Trigger: the name lands on Select.Root (so the hidden form input picks it up) and the trigger reads the ARIA props from context.
Composition
Compose the parts of a Select together to build your own:
Polymorphism
Select.Trigger, Select.Item, Select.Group, Select.Label, and Select.Separator accept asChild, which swaps the part's default element for your single child while keeping its styling and the select's keyboard and selection behavior. Select.Label asChild helps when a group heading must be a specific element for the page's outline. Select.Trigger asChild makes your own control the combobox, and Select.Item asChild makes your own element the option. Each part renders its own chrome beside your children (the trigger's caret, the item's check indicator), so Radix clones your single child as the part's element and moves the label span and that chrome inside it. The translation guarantee holds on both paths.
Select.Root is a state provider that renders no DOM, so it takes no asChild.
Select.Content takes no asChild, and its props type omits it. It renders the scroll buttons and the viewport around your items, so there is no single child for a slot to clone. Style it with className instead.
Layering and z-index
Select.Content renders at Tailwind z-50, Mantle's float tier. When composed inside an open Dialog, AlertDialog, or Sheet, it portals into that overlay's positioner and paints above the overlay that owns it. Outside every overlay, it portals to document.body, below the overlay tier (z-60). When sibling floats share a container, the most recently mounted float paints on top.
Select does not render a backdrop. Modal layers such as Dialog, Sheet, and AlertDialog do render backdrops, so stacking those layers compounds the dim and blur effect on the layers underneath.
Examples
With TanStack Form
Use @tanstack/react-form with zod to keep Select.Root controlled. Wrap the root in Field.Control so the generated name, id, and ARIA props reach the select trigger and hidden input.
Raw usage and validation
Use Select.Root directly when you don't need Field label, helper, or error wiring. Set validation on Select.Root to render a success, warning, or error state outside of a field.
Custom selected value
By default the selected item's text renders in the trigger. To render something different, control the select and pass children instead.
Rich item labels
An item takes any content, and the trigger shows the selected label as-is. When the list needs two lines and the trigger must stay on one line, control the select and render a compact value with Select.Value children. Both renders stay safe on a translated page: React removes the label span or the value span, never a bare text node.
Untranslatable labels
When a label is an ID, a path, a filename, or a key, set translate="no" on the item. A browser translation engine then skips the row and the copy the trigger shows, so the reader copies the right string.
API Reference
The Select components are built on top of Radix Select.
Select.Root
Displays a list of options for the user to pick from—triggered by a button.
Select.Content renders at Tailwind z-50, Mantle's float tier. When composed inside an open Dialog, AlertDialog, or Sheet, it portals into that overlay's positioner and paints above the overlay that owns it. Outside every overlay, it portals to document.body, below the overlay tier (z-60). When sibling floats share a container, the most recently mounted float paints on top.
Set validation on Select.Root when the whole select has an explicit state; this root-level state is forwarded to the trigger and takes precedence over ambient field-level validation from Field.Item.
Rendered Field.Errors or Field.ErrorList still force the trigger into the error state because Field.Control sets aria-invalid="true" on the focusable trigger, and an explicit invalid ARIA value always wins. If a non-error Select.Root state needs to win even when errors are rendered, pass validation on Field.Item to suppress the inferred error.
All props from Radix Select.Root, plus:
Select.Trigger
The button that toggles the Select. The Select.Content will position itself adjacent to the trigger.
When composing with Field.Item, wrap Select.Root (not the trigger) in Field.Control. The generated id, name, and aria-invalid flow onto Select.Root (so the hidden form input picks up the field name), and the trigger reads aria-describedby / aria-errormessage from FieldControlContext. Validation resolves from explicit Select.Root, then Select.Trigger, then the nearest Field.Item.
children render inside a <span data-slot="select-trigger-label">. The caret is a permanent element sibling, so without the span a bare text child is never a lone child: a browser translation engine reparents that text node, and the removal React runs when the child changes shape or goes away throws. The span is display: contents, so Select.Value stays a flex item of the trigger and keeps its gap. Browser translation covers the mechanism and the rules your own components need.
Select.Value is now a grandchild of the trigger, so a [&>span] class of your own reaches the wrapper instead. Match the part's own variant: [&>[data-slot=select-trigger-label]>span]:line-clamp-none. That form shares the default's variant prefix, so tailwind-merge drops the default instead of shipping both. A bare [&>span]:line-clamp-none lands on the wrapper, and line-clamp sets display, which gives the wrapper a box and takes Select.Value out of the trigger's flex row.
All props from Radix Select.Trigger, plus:
Data attributes
Select.Value
The part that reflects the selected value. Renders the selected item's text by default. For more control, control the select and pass your own children. Do not style this part: under Select.Content position="item-aligned", Radix measures its box to align the open list over the selected item. Pass placeholder for the text to show when the select has no value.
placeholder renders inside a <span data-slot="select-placeholder">, your own children inside a <span data-slot="select-value-label">, and the selected item's text arrives inside the select-item-label span from Select.Item. Each span is display: contents, so it adds no box and changes no layout. React removes one of those spans whenever the value changes, and an element removal is safe on a translated page. A browser translation engine reparents text nodes, and a removal aimed at a reparented text node throws. Browser translation covers the mechanism.
Radix Select.Value props.
Data attributes
Select.Content
The component that pops out when the Select is open as a portal adjacent to the Select.Trigger button. It contains a scrolling viewport of the select items. position defaults to "popper", so the list opens below the trigger instead of over the selected item.
Select.Content renders at Tailwind z-50, Mantle's float tier. When composed inside an open Dialog, AlertDialog, or Sheet, it portals into that overlay's positioner and paints above the overlay that owns it. Outside every overlay, it portals to document.body, below the overlay tier (z-60). When sibling floats share a container, the most recently mounted float paints on top.
All props from Radix Select.Content, except asChild, plus:
Data attributes
Select.Group
A group of related options within a select menu. Similar to an html optgroup element. Pair it with Select.Label, which gives the group its accessible name.
Radix Select.Group props.
Data attributes
Select.Separator
Used to visually separate items in the select. Composed from Mantle Separator.
Data attributes
Select.Item
An option within a select menu. Similar to an html option element. Has a required value prop. When a user selects this item, Select.Root passes that value to its onValueChange handler. Displays the children as the option's text. Pass icon to render an icon before the text.
children render inside a <span data-slot="select-item-label">. Radix portals that span into Select.Value while the item is selected and removes it when the selection changes. The span exists so the node React removes is an element: a browser translation engine reparents text nodes, and a removal aimed at one throws. The span is display: contents, so it adds no box in the list or in the trigger, and your own item content stays the direct layout child of the value. To style it as a box, set a display of your own on it first. Browser translation covers the mechanism and the rule your own components need.
When the label is an ID, a path, a filename, or a key, set translate="no" on the item. The attribute also rides on the label span, so the copy the trigger shows stays untranslated too.
All props from Radix Select.Item, plus:
Data attributes
Select.Label
Used to render the label of a group. It won't be focusable using arrow keys. Pair it with Select.Group — the label gives the group its accessible name.
Radix Select.Label props.