Select
Source codeChoose one or more values from a dropdown menu.
import { Select } from "@/ui/select";
const fonts = [
{ label: "Inter", value: "inter" },
{ label: "Berkeley Mono", value: "berkeley-mono" },
{ label: "Libre Baskerville", value: "libre-baskerville" },
];
export default function SelectDemo() {
return (
<Select.Root items={fonts}>
<Select.Trigger>
<Select.Value placeholder="Select a font" />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.ScrollUpArrow />
<Select.List>
{fonts.map((font) => (
<Select.Item key={font.value} value={font.value}>
<Select.ItemIndicator />
<Select.ItemText>{font.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
<Select.ScrollDownArrow />
</Select.Popup>
</Select.Root>
);
}A trigger opens a list of options and shows the chosen value.
Sizes
Three sizes to fit different layouts.
import { Select } from "@/ui/select";
const themes = [
{ label: "System", value: "system" },
{ label: "Light", value: "light" },
{ label: "Dark", value: "dark" },
];
const sizes = ["sm", "md", "lg"] as const;
export default function SelectSizesDemo() {
return (
<div className="flex flex-wrap items-end gap-3">
{sizes.map((size) => (
<Select.Root key={size} items={themes} defaultValue="system">
<Select.Trigger size={size}>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{themes.map((theme) => (
<Select.Item key={theme.value} value={theme.value}>
<Select.ItemIndicator />
<Select.ItemText>{theme.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
))}
</div>
);
}Multiple
Let users choose more than one option.
"use client";
import { Select } from "@/ui/select";
const languages = [
{ label: "TypeScript", value: "ts" },
{ label: "Rust", value: "rust" },
{ label: "Go", value: "go" },
{ label: "Python", value: "py" },
{ label: "Swift", value: "swift" },
];
function renderValue(value: string[]) {
if (value.length === 0) {
return "Select languages";
}
if (value.length === 1) {
return languages.find((language) => language.value === value[0])?.label;
}
return `${value.length} selected`;
}
export default function SelectMultipleDemo() {
return (
<Select.Root items={languages} multiple defaultValue={["ts", "rust"]}>
<Select.Trigger>
<Select.Value>{renderValue}</Select.Value>
<Select.Icon />
</Select.Trigger>
<Select.Popup alignItemWithTrigger={false}>
<Select.List>
{languages.map((language) => (
<Select.Item key={language.value} value={language.value}>
<Select.ItemIndicator />
<Select.ItemText>{language.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
);
}Grouped
Organize options into labeled sections.
import { Fragment } from "react";
import { Select } from "@/ui/select";
const groups = [
{
value: "Fruits",
items: [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
],
},
{
value: "Vegetables",
items: [
{ label: "Carrot", value: "carrot" },
{ label: "Spinach", value: "spinach" },
],
},
];
export default function SelectGroupedDemo() {
return (
<Select.Root items={groups}>
<Select.Trigger>
<Select.Value placeholder="Select produce" />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{groups.map((group, index) => (
<Fragment key={group.value}>
<Select.Group>
<Select.GroupLabel>{group.value}</Select.GroupLabel>
{group.items.map((item) => (
<Select.Item key={item.value} value={item.value}>
<Select.ItemIndicator />
<Select.ItemText>{item.label}</Select.ItemText>
</Select.Item>
))}
</Select.Group>
{index < groups.length - 1 && <Select.Separator />}
</Fragment>
))}
</Select.List>
</Select.Popup>
</Select.Root>
);
}Icons
Show an icon next to each option.
import { Icon } from "@/ui/icon";
import { Select } from "@/ui/select";
const appearances = [
{
value: "system",
label: (
<>
<Icon name="Monitor" />
System
</>
),
},
{
value: "light",
label: (
<>
<Icon name="Sun" />
Light
</>
),
},
{
value: "dark",
label: (
<>
<Icon name="Moon" />
Dark
</>
),
},
];
export default function SelectIconsDemo() {
return (
<Select.Root items={appearances} defaultValue="system">
<Select.Trigger>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{appearances.map((appearance) => (
<Select.Item key={appearance.value} value={appearance.value}>
<Select.ItemIndicator />
<Select.ItemText>{appearance.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
);
}Avatars
Show an avatar next to each option.
import { Avatar } from "@/ui/avatar";
import { Select } from "@/ui/select";
const people = [
{ value: "lara", name: "Lara Tucci", initials: "LT", src: "https://i.pravatar.cc/80?img=5" },
{ value: "devon", name: "Devon Lane", initials: "DL", src: "https://i.pravatar.cc/80?img=12" },
{ value: "noah", name: "Noah Pierre", initials: "NP", src: "https://i.pravatar.cc/80?img=33" },
];
const items = people.map((person) => ({
value: person.value,
label: (
<>
<Avatar.Root data-slot="icon">
<Avatar.Image src={person.src} alt={person.name} />
<Avatar.Fallback>{person.initials}</Avatar.Fallback>
</Avatar.Root>
{person.name}
</>
),
}));
export default function SelectAvatarsDemo() {
return (
<Select.Root items={items} defaultValue="lara">
<Select.Trigger>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{items.map((item) => (
<Select.Item key={item.value} value={item.value}>
<Select.ItemIndicator />
<Select.ItemText>{item.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
);
}Disabled
Disable the whole control or individual options.
import { Select } from "@/ui/select";
const plans = [
{ label: "Hobby", value: "hobby" },
{ label: "Pro", value: "pro" },
{ label: "Enterprise", value: "enterprise" },
];
export default function SelectDisabledDemo() {
return (
<div className="flex flex-wrap items-center gap-3">
{/* The whole control is disabled. */}
<Select.Root items={plans} defaultValue="pro" disabled>
<Select.Trigger>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{plans.map((plan) => (
<Select.Item key={plan.value} value={plan.value}>
<Select.ItemIndicator />
<Select.ItemText>{plan.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
{/* A single option is disabled. */}
<Select.Root items={plans} defaultValue="hobby">
<Select.Trigger>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{plans.map((plan) => (
<Select.Item
key={plan.value}
value={plan.value}
disabled={plan.value === "enterprise"}
>
<Select.ItemIndicator />
<Select.ItemText>{plan.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
</div>
);
}Invalid
Shows an error state when the value is invalid.
import { Select } from "@/ui/select";
const sizes = [
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
];
export default function SelectInvalidDemo() {
return (
<Select.Root items={sizes}>
<Select.Trigger data-invalid>
<Select.Value placeholder="Select a size" />
<Select.Icon />
</Select.Trigger>
<Select.Popup>
<Select.List>
{sizes.map((size) => (
<Select.Item key={size.value} value={size.value}>
<Select.ItemIndicator />
<Select.ItemText>{size.label}</Select.ItemText>
</Select.Item>
))}
</Select.List>
</Select.Popup>
</Select.Root>
);
}API
Select.Trigger
| Prop | Type | Default |
|---|---|---|
| size | "sm" | "md" | "lg" | "md" |
Select.Popup
| Prop | Type | Default |
|---|---|---|
| sideOffset | number | 4 |
| side | "top" | "bottom" | "left" | "right" | "bottom" |
| align | "start" | "center" | "end" | "center" |
| alignItemWithTrigger | boolean | true |
| container | HTMLElement | Ref | null | - |
Plus everything from Base UI Select, including items, value, defaultValue, and onValueChange. Select.Popup forwards the remaining positioner props.