Select
A control for choosing from a known, closed list — statuses, protocols, modalities.
Installation
npx shadcn@latest add https://qure-ui.qure.ai/r/select.jsonUsage
import {
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
} from '@/components/ui/select'const statuses = { preliminary: 'Preliminary', final: 'Final' }
<Select items={statuses} defaultValue="preliminary">
<SelectTrigger className="w-56" aria-label="Report status">
<SelectValue placeholder="Select a status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="preliminary">Preliminary</SelectItem>
<SelectItem value="final">Final</SelectItem>
</SelectContent>
</Select>Pass items whenever the select starts with a value. The popup is not mounted until it opens,
so SelectValue has no labels to look up and shows the raw value — a trigger reading
in-progress rather than In progress. items takes the same map you can render the list
from, so the two cannot drift apart.
Reach for a select when the list is closed and the reader is not expected to know it by heart.
Two or three mutually exclusive options are usually better as a RadioGroup, where all of them
are visible at once. A list long enough to need searching wants a combobox, not this.
Composition
<Select> {/* owns the value and the open state */}
<SelectTrigger> {/* the button; renders the chevron itself */}
<SelectValue> {/* the chosen label, or the placeholder */}
</SelectTrigger>
<SelectContent> {/* portal + positioner + popup + list */}
<SelectGroup> {/* an optional section */}
<SelectLabel> {/* its heading, tied to the group by aria */}
<SelectItem> {/* one option; renders its own tick */}
</SelectGroup>
<SelectSeparator> {/* a rule between groups */}
</SelectContent>
</Select>SelectTrigger adds its own Select.Icon, and SelectItem its own Select.ItemText and
Select.ItemIndicator, so the common case stays three levels deep rather than six.
SelectContent bundles the portal, the positioner and the two scroll arrows that appear when the
list is taller than the space beneath the trigger.
The value lives on the root. defaultValue for uncontrolled, value plus onValueChange for
controlled — and name if the select is inside a form, which renders a hidden input.
Sizes
The same 32/40/48 scale as Input, so a select and a text field on one row line up. md is the
default and the right choice unless the row is dense.
Groups and separators
Group when the list has real sections — body part, modality, site. A disabled item still says why it cannot be picked, which is more use to the reader than an option that has silently vanished.
In a field
Locked once the report is signed.
A placeholder is not a label. "Not set" inside the trigger disappears the moment a value is
chosen, and it is never read as the field's name — put a Label above it, or an aria-label
on the trigger when the layout genuinely has no room.
The disabled select keeps its value visible rather than emptying: a locked field still has to report what it is locked to.
Choosing several
The popup stays open as findings are ticked, so a list can be built in one pass.
multiple turns each item into a toggle and leaves the popup open, which is what you want when
someone is assembling a list. Pass a function to SelectValue to summarise the selection —
without it the trigger shows every label, and five findings will not fit.
How selection reads
Base UI puts data-selected on the chosen item and data-highlighted on the one under the
cursor, keyboard or pointer:
.cn-select-item[data-highlighted] { background-color: var(--background-brand-tertiary); }
.cn-select-item[data-selected] { font-weight: 600; }The Figma popup marks the selected option with weight 600 and nothing more. We keep that and add a tick, because weight alone is only legible next to its neighbours — and the selected row is often the only one on screen.
Keyboard
Space or Enter opens, typing jumps to a matching option, arrows move the highlight, Enter picks it, Escape closes without changing anything.
API Reference
Everything Base UI's Select accepts. What we add
on SelectTrigger:
Prop
Type
On SelectContent:
Prop
Type
On Select itself:
Prop
Type