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 https://herocn.dev/r/select.jsonimport {
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" |
See the Base UI Select API reference for all other props (items, value, onValueChange, positioning, etc.).