Components

Select

Choose one or more options from a short list in a popup.

Loading...

Installation

npx shadcn@latest add @fujin/select

Usage

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"
const themes = [
  { value: "light", label: "Light" },
  { value: "dark", label: "Dark" },
  { value: "system", label: "System" },
]
 
<Select items={themes}>
  <SelectTrigger aria-label="Theme">
    <SelectValue placeholder="Select a theme" />
  </SelectTrigger>
  <SelectContent>
    {themes.map((theme) => (
      <SelectItem key={theme.value} value={theme.value}>
        {theme.label}
      </SelectItem>
    ))}
  </SelectContent>
</Select>

Pass items to Select. It is how SelectValue finds the label for the selected value; without it the trigger shows the raw value ("light") until you give SelectValue a formatter function as its child.

In a form

<Field name="region">
  <FieldLabel nativeLabel={false} render={<div />}>
    Region
  </FieldLabel>
  <Select items={regions}>
    <SelectTrigger className="w-full">
      <SelectValue placeholder="Select a region" />
    </SelectTrigger>
    <SelectContent>...</SelectContent>
  </Select>
  <FieldDescription>Where your data is stored.</FieldDescription>
</Field>

Field labels and describes the trigger and submits the value under its name. A plain <FieldLabel> works too, but because it is a native <label>, clicking it also opens the popup; nativeLabel={false} render={<div />} makes a label click only focus the trigger, like a native <select>.

By default the popup overlaps the trigger so the selected item sits exactly over the trigger text (alignItemWithTrigger). In that mode side and align are ignored. It falls back to a normal dropdown on touch input, or when there is not enough room. Pass alignItemWithTrigger={false} to SelectContent for a dropdown below the trigger - usually better for multiple. Scroll arrows appear at the top and bottom of long lists.

Multiple selection

Loading...
<Select items={channels} multiple value={value} onValueChange={setValue}>
  <SelectTrigger aria-label="Notification channels">
    <SelectValue placeholder="No channels" />
  </SelectTrigger>
  <SelectContent alignItemWithTrigger={false}>...</SelectContent>
</Select>

The trigger shows the selected labels comma-separated. For long lists, or when users need to see each selection, use combobox with chips.

Groups

<SelectContent>
  <SelectGroup>
    <SelectLabel>Europe</SelectLabel>
    <SelectItem value="eu-west">EU West (Ireland)</SelectItem>
  </SelectGroup>
  <SelectSeparator />
  ...
</SelectContent>

What's different from shadcn/ui

  • items on Select replaces Radix's automatic label lookup; see Usage.
  • onValueChange receives null when nothing is selected.
  • SelectLabel is a group heading (Base UI GroupLabel), as in shadcn - label the whole select with FieldLabel or aria-label.
  • SelectTrigger has solid focus and invalid rings, and picks up data-invalid from a surrounding Field.

API Reference

Prop

Type

Every other prop is forwarded to the Base UI Select.

On this page