import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";
const items = [ { label: "Select a fruit", value: null }, { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" },];
export function SelectDemo() { return ( <Select items={items}> <SelectTrigger className="w-full max-w-48"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> );}pnpm dlx shadcn@latest add @herocn/selectimport { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@/components/ui/select"Pass an items array to Select so SelectValue can show the label for the selected option. Render SelectItem children for each option (including groups and separators as needed).
const items = [ { label: "Select a fruit", value: null }, { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" },]
<Select items={items}> <SelectTrigger className="w-full max-w-48"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent></Select>Use the following composition to build a Select:
Select├── SelectTrigger│ └── SelectValue└── SelectContent ├── SelectGroup │ ├── SelectLabel │ ├── SelectItem │ └── SelectItem ├── SelectSeparator └── SelectGroup ├── SelectLabel ├── SelectItem └── SelectItemUse SelectGroup, SelectLabel, and SelectSeparator to organize items.
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@/components/ui/select";
export function SelectGroups() { const fruits = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, ]; const vegetables = [ { label: "Carrot", value: "carrot" }, { label: "Broccoli", value: "broccoli" }, { label: "Spinach", value: "spinach" }, ]; const allItems = [ { label: "Select a fruit", value: null }, ...fruits, ...vegetables, ]; return ( <Select items={allItems}> <SelectTrigger className="w-full max-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> );}Set multiple on Select and control selection with an array value and onValueChange.
"use client";
import * as React from "react";import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";
const fruits = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" },];
export function SelectMultiple() { const [value, setValue] = React.useState<string[]>([]);
return ( <Select multiple items={fruits} value={value} onValueChange={setValue}> <SelectTrigger className="w-full max-w-sm"> <SelectValue placeholder="Select fruits..." /> </SelectTrigger> <SelectContent> <SelectGroup> {fruits.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> );}A select with many items scrolls inside the popup.
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue,} from "@/components/ui/select";
const northAmerica = [ { label: "Eastern Standard Time", value: "est" }, { label: "Central Standard Time", value: "cst" }, { label: "Mountain Standard Time", value: "mst" }, { label: "Pacific Standard Time", value: "pst" }, { label: "Alaska Standard Time", value: "akst" }, { label: "Hawaii Standard Time", value: "hst" },];
const europeAfrica = [ { label: "Greenwich Mean Time", value: "gmt" }, { label: "Central European Time", value: "cet" }, { label: "Eastern European Time", value: "eet" }, { label: "Western European Summer Time", value: "west" }, { label: "Central Africa Time", value: "cat" }, { label: "East Africa Time", value: "eat" },];
const asia = [ { label: "Moscow Time", value: "msk" }, { label: "India Standard Time", value: "ist" }, { label: "China Standard Time", value: "cst_china" }, { label: "Japan Standard Time", value: "jst" }, { label: "Korea Standard Time", value: "kst" }, { label: "Indonesia Central Standard Time", value: "ist_indonesia" },];
const australiaPacific = [ { label: "Australian Western Standard Time", value: "awst" }, { label: "Australian Central Standard Time", value: "acst" }, { label: "Australian Eastern Standard Time", value: "aest" }, { label: "New Zealand Standard Time", value: "nzst" }, { label: "Fiji Time", value: "fjt" },];
const southAmerica = [ { label: "Argentina Time", value: "art" }, { label: "Bolivia Time", value: "bot" }, { label: "Brasilia Time", value: "brt" }, { label: "Chile Standard Time", value: "clt" },];
const items = [ { label: "Select a timezone", value: null }, ...northAmerica, ...europeAfrica, ...asia, ...australiaPacific, ...southAmerica,];
export function SelectScrollable() { return ( <Select items={items}> <SelectTrigger className="w-full max-w-64"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>North America</SelectLabel> {northAmerica.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> <SelectGroup> <SelectLabel>Europe & Africa</SelectLabel> {europeAfrica.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> <SelectGroup> <SelectLabel>Asia</SelectLabel> {asia.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> <SelectGroup> <SelectLabel>Australia & Pacific</SelectLabel> {australiaPacific.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> <SelectGroup> <SelectLabel>South America</SelectLabel> {southAmerica.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> );}Set disabled on the root Select to disable the whole control, or on individual SelectItem components to disable specific options.
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";
const items: Array<{ label: string; value: string | null; disabled?: boolean;}> = [ { label: "Select a fruit", value: null }, { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes", disabled: true }, { label: "Pineapple", value: "pineapple" },];
export function SelectDisabled() { return ( <Select items={items} disabled> <SelectTrigger className="w-full max-w-48"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value} disabled={item.disabled} > {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> );}Add the data-invalid attribute to the Field component and the aria-invalid attribute to the SelectTrigger component to show an error state.
<Field data-invalid> <FieldLabel>Fruit</FieldLabel> <SelectTrigger aria-invalid> <SelectValue /> </SelectTrigger></Field>import { Field, FieldError, FieldLabel } from "@/components/ui/field";import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";
const items = [ { label: "Select a fruit", value: null }, { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" },];
export function SelectInvalid() { return ( <Field data-invalid className="w-full max-w-48"> <FieldLabel>Fruit</FieldLabel> <Select items={items}> <SelectTrigger aria-invalid> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> <FieldError>Please select a fruit.</FieldError> </Field> );}The Select component supports two visual variants:
primary (default) - Standard styling with shadow, suitable for most use casessecondary - Lower emphasis variant without shadow, suitable for use in Surface componentsimport { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";
const items = [ { label: "Select a fruit", value: null }, { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" },];
export function SelectVariants() { return ( <div className="flex w-full max-w-sm flex-col gap-4"> <Select items={items} variant="default"> <SelectTrigger className="w-full"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> <Select items={items} variant="secondary"> <SelectTrigger className="w-full"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> </div> );}Place the select inside a Surface panel. Use variant="secondary" on Select so the trigger sits cleanly on the surface background.
Choose how the interface looks on this device.
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";import { Surface } from "@/components/ui/surface";
const items = [ { label: "Select a theme", value: null }, { label: "Light", value: "light" }, { label: "Dark", value: "dark" }, { label: "System", value: "system" },];
export function SelectSurface() { return ( <Surface className="w-full max-w-sm rounded-3xl border p-4"> <h4 className="font-medium text-sm">Appearance</h4> <p className="mt-1 text-muted-foreground text-sm"> Choose how the interface looks on this device. </p> <div className="mt-4"> <Select items={items} variant="secondary"> <SelectTrigger className="w-full"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.label} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> </div> </Surface> );}To enable RTL support in shadcn/ui, see the RTL configuration guide.
"use client";
import * as React from "react";
import { type Translations, useTranslation,} from "@/components/language-selector";import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@/components/ui/select";
const translations: Translations = { en: { dir: "ltr", values: { selectFruit: "Select a fruit", fruits: "Fruits", apple: "Apple", banana: "Banana", blueberry: "Blueberry", grapes: "Grapes", pineapple: "Pineapple", vegetables: "Vegetables", carrot: "Carrot", broccoli: "Broccoli", spinach: "Spinach", }, }, ar: { dir: "rtl", values: { selectFruit: "اختر فاكهة", fruits: "الفواكه", apple: "تفاح", banana: "موز", blueberry: "توت أزرق", grapes: "عنب", pineapple: "أناناس", vegetables: "الخضروات", carrot: "جزر", broccoli: "بروكلي", spinach: "سبانخ", }, }, he: { dir: "rtl", values: { selectFruit: "בחר פרי", fruits: "פירות", apple: "תפוח", banana: "בננה", blueberry: "אוכמניה", grapes: "ענבים", pineapple: "אננס", vegetables: "ירקות", carrot: "גזר", broccoli: "ברוקולי", spinach: "תרד", }, },};
export function SelectRtl() { const { dir, t, language } = useTranslation(translations, "ar"); const [selectedFruit, setSelectedFruit] = React.useState<string | null>(null);
const fruits = [ { label: t.apple, value: "apple" }, { label: t.banana, value: "banana" }, { label: t.blueberry, value: "blueberry" }, { label: t.grapes, value: "grapes" }, { label: t.pineapple, value: "pineapple" }, ];
const vegetables = [ { label: t.carrot, value: "carrot" }, { label: t.broccoli, value: "broccoli" }, { label: t.spinach, value: "spinach" }, ];
const allItems = [ { label: t.selectFruit, value: null }, ...fruits, ...vegetables, ];
return ( <Select items={allItems} value={selectedFruit} data-lang={language} onValueChange={setSelectedFruit} > <SelectTrigger data-lang={dir === "rtl" ? language : undefined} className="w-auto min-w-32" dir={dir} > <SelectValue /> </SelectTrigger> <SelectContent dir={dir} data-lang={dir === "rtl" ? language : undefined}> <SelectGroup> <SelectLabel>{t.fruits}</SelectLabel> {fruits.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> <SelectSeparator /> <SelectGroup> <SelectLabel>{t.vegetables}</SelectLabel> {vegetables.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> );}The select is built on Base UI Select. Select forwards props to the root; SelectTrigger adds styling and a size option.
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "secondary" | "default" |
Sets the trigger background style via context (secondary uses the secondary surface). | ||
See the Base UI Select API reference for all other props (items, value, onValueChange, positioning, etc.).