Select
A dropdown for picking one value from a list, built on Base UI. Driven by an items array, with three trigger variants and four sizes.
Playground
<Select items={fruits}>
<SelectTrigger className="w-48">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
{fruits.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>Preview
Pass items (an array of label / value objects) to Select, then map them to SelectItems inside SelectContent. An item with value: null acts as the placeholder row.
const fruits = [
{ label: "Select a fruit", value: null },
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Blueberry", value: "blueberry" },
]
<Select items={fruits}>
<SelectTrigger className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Fruits</SelectLabel>
{fruits.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>Installation
npx shadcn@latest add https://espresso-base-ui.vercel.app/r/select.jsonUsage
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"<Select items={items} defaultValue="apple">
<SelectTrigger className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent>
{items.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>Items with icons
Add an icon component to any entry in the items array and it renders automatically: SelectItem looks its value up in the shared items context and prepends the icon, and a childless SelectValue renders the selected item's icon next to its label in the trigger.
const fruitsWithIcons = [
{ label: "Select fruit", value: null, icon: Diamond },
{ label: "Apple", value: "apple", icon: Apple },
{ label: "Banana", value: "banana", icon: Banana },
]
<Select items={fruitsWithIcons}>
<SelectTrigger className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent>
{fruitsWithIcons.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>Variants
variant on the trigger picks the surface: outline (bordered shadow, the default), subtle (filled secondary) and ghost (transparent until hovered).
<SelectTrigger variant="outline" className="w-48">
<SelectValue placeholder="Outline" />
</SelectTrigger>
<SelectTrigger variant="subtle" className="w-48">
<SelectValue placeholder="Subtle" />
</SelectTrigger>
<SelectTrigger variant="ghost" className="w-48">
<SelectValue placeholder="Ghost" />
</SelectTrigger>Sizes
size sets the trigger height and radius: xs (24px, rounded-sm), sm (28px, rounded-md), md (32px, rounded-md, the default) and lg (40px, rounded-lg with larger text).
<SelectTrigger size="xs" className="w-48"><SelectValue /></SelectTrigger>
<SelectTrigger size="sm" className="w-48"><SelectValue /></SelectTrigger>
<SelectTrigger size="md" className="w-48"><SelectValue /></SelectTrigger>
<SelectTrigger size="lg" className="w-48"><SelectValue /></SelectTrigger>Groups
Wrap related items in SelectGroup with a SelectLabel, and divide groups with SelectSeparator. Long lists scroll, with gradient scroll arrows built into the popup.
<Select items={[...fruits, ...vegetables]}>
<SelectTrigger className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Fruits</SelectLabel>
{fruits.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Vegetables</SelectLabel>
{vegetables.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>API reference
Select wraps the Base UI select root and shares the items array with its descendants via context – that is what powers automatic labels and icons. Each item is { label: string; value: string | null; icon?: Component }.
Select
| Prop | Type | Default | Description |
|---|---|---|---|
| items | SelectOption[] | – | Option list shared via context; used by SelectValue and SelectItem to resolve labels and icons. |
| value | string | null | – | Controlled selected value. |
| defaultValue | string | null | – | Initially selected value when uncontrolled. |
| onValueChange | (value, eventDetails) => void | – | Called when the selection changes. |
| disabled | boolean | false | Disables the whole select. |
| name | string | – | Name submitted with the owning form (renders a hidden input). |
SelectTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "outline" | "subtle" | "ghost" | "outline" | Trigger surface – bordered shadow, filled secondary, or transparent. |
| size | "xs" | "sm" | "md" | "lg" | "md" | Trigger height (24 / 28 / 32 / 40px) and radius (sm / md / md / lg). |
| suffix | React.ReactElement | <ChevronDownIcon /> | Custom trailing icon rendered through the select's Icon slot. |
SelectContent
| Prop | Type | Default | Description |
|---|---|---|---|
| side | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "bottom" | Side of the trigger the popup opens on. |
| sideOffset | number | 4 | Gap between the trigger and the popup. |
| align | "start" | "center" | "end" | "center" | Alignment along the trigger edge. |
| alignOffset | number | 0 | Extra offset along the alignment axis. |
| alignItemWithTrigger | boolean | true | Overlaps the popup so the selected item lines up with the trigger (native-select style). Set false for plain dropdown positioning with slide animations. |
| Component | Description |
|---|---|
| SelectValue | Selected-value display inside the trigger (data-slot="select-value"). With no children and an items array, renders the selected item's icon and label; accepts a placeholder prop. |
| SelectGroup | Groups related items (data-slot="select-group") for labelling. |
| SelectLabel | Non-interactive group heading (data-slot="select-label"). |
| SelectItem | A selectable option (data-slot="select-item"). Auto-prepends the matching items-array icon and shows a check indicator when selected. Supports disabled. |
| SelectSeparator | Thin divider between groups (data-slot="select-separator"). |
| SelectScrollUpButton / SelectScrollDownButton | Gradient scroll arrows pinned to the popup edges (data-slot="select-scroll-up-button" / "select-scroll-down-button"); rendered automatically by SelectContent. |
Accessibility & styling hooks
Base UI provides the combobox/listbox semantics, typeahead and full keyboard navigation, plus a hidden input for form submission. The trigger reflects data-variant, data-size, data-placeholder and data-disabled, and styles data-invalid/data-valid with error and success outlines – it also inherits invalid styling from a surrounding Field data-invalid="true". Items expose data-highlighted and data-disabled, and every part carries a data-slot attribute for CSS targeting.