Select

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

placeholder
variant
size
icons
<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.json

Usage

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

PropTypeDefaultDescription
itemsSelectOption[]–Option list shared via context; used by SelectValue and SelectItem to resolve labels and icons.
valuestring | null–Controlled selected value.
defaultValuestring | null–Initially selected value when uncontrolled.
onValueChange(value, eventDetails) => void–Called when the selection changes.
disabledbooleanfalseDisables the whole select.
namestring–Name submitted with the owning form (renders a hidden input).

SelectTrigger

PropTypeDefaultDescription
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).
suffixReact.ReactElement<ChevronDownIcon />Custom trailing icon rendered through the select's Icon slot.

SelectContent

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left" | "inline-start" | "inline-end""bottom"Side of the trigger the popup opens on.
sideOffsetnumber4Gap between the trigger and the popup.
align"start" | "center" | "end""center"Alignment along the trigger edge.
alignOffsetnumber0Extra offset along the alignment axis.
alignItemWithTriggerbooleantrueOverlaps the popup so the selected item lines up with the trigger (native-select style). Set false for plain dropdown positioning with slide animations.
ComponentDescription
SelectValueSelected-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.
SelectGroupGroups related items (data-slot="select-group") for labelling.
SelectLabelNon-interactive group heading (data-slot="select-label").
SelectItemA selectable option (data-slot="select-item"). Auto-prepends the matching items-array icon and shows a check indicator when selected. Supports disabled.
SelectSeparatorThin divider between groups (data-slot="select-separator").
SelectScrollUpButton / SelectScrollDownButtonGradient 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.

Search documentation

Search the component docs