Components
Select
Single-choice picker for a known list. Above ~7 options, prefer a Combobox pattern (not shipped yet).
When to usewhen the operator picks one value from a short, closed list. If the list is long or needs filtering, use Combobox.
Import
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@ravnhq/efni/select"Default
<Select defaultValue="planning">
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="planning">Planning</SelectItem>
<SelectItem value="execute">Executing</SelectItem>
</SelectContent>
</Select>Grouped
<SelectGroup>
<SelectLabel>Active tools</SelectLabel>
<SelectItem value="mb">Mission Board</SelectItem>
</SelectGroup>Do & don't
Pair the trigger with a Label — the value alone rarely reads as a field.
Don't ship a bare Select in a form — its purpose isn't legible without a label.
Accessibility
| Key | Action |
|---|---|
| Space / Enter | Open the menu; commit on close. |
| ↑ / ↓ | Move highlight between items. |
| Home / End | Jump to first / last item. |
| A – Z | Type-ahead: jump to matching item. |
| Esc | Close without changing the value. |
- Wrap group items in a
SelectGroup;SelectLabeloutside a group throwsMenuGroupContext is missing. - Trigger renders a native button — focus and semantics come for free.
Guidelines
Do
- Use for known, closed lists — status, phase, tool, priority.
- Always pair with a
Labelor aria-label; screen readers announce the value, not the field name. - Group with SelectGroup + SelectLabel when categories exist.
Don't
- Don't use for 8+ options — reach for Combobox for searchability.
- Don't nest a Select inside a Table cell — use inline text or a Popover.
- Don't put icons or badges next to the value in the trigger; they compete with the caret.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | SelectValueType<Value, Multiple> | null | — | The uncontrolled value of the select when it's initially rendered. To render a controlled select, use the value prop instead. |
value | SelectValueType<Value, Multiple> | null | — | The value of the select. Use when controlled. |
onValueChange | ((value: SelectValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void) | — | Event handler called when the value of the select changes. |
disabled | boolean | false | Whether the component should ignore user interaction. |
SelectTrigger.size | "default" | "sm" | "default" | Trigger height. Use sm inside a dense table row. |
…props | SelectPrimitive.Trigger.Props | — | Everything SelectTrigger passes to the element it renders. |