Select
Displays a list of options for the user to pick from, triggered by a button.
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const fruits = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Cherry', value: 'cherry' },
];
export default function SelectDemo() {
return (
<Select items={fruits}>
<SelectTrigger>
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
{fruits.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
Install
npx shadcn@latest add @madeui/select
Usage
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
Composition
<Select>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem />
</SelectContent>
</Select>
Groups
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const groups = [
{
label: 'Fruits',
items: [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Cherry', value: 'cherry' },
],
},
{
label: 'Vegetables',
items: [
{ label: 'Carrot', value: 'carrot' },
{ label: 'Potato', value: 'potato' },
{ label: 'Onion', value: 'onion' },
],
},
];
const items = groups.flatMap((group) => group.items);
export default function SelectGroups() {
return (
<Select items={items}>
<SelectTrigger>
<SelectValue placeholder="Select a food" />
</SelectTrigger>
<SelectContent>
{groups.map((group, index) => (
<SelectGroup key={group.label}>
{index > 0 && <SelectSeparator />}
<SelectLabel>{group.label}</SelectLabel>
{group.items.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectGroup>
))}
</SelectContent>
</Select>
);
}
Scrollable
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const timezones = [
'UTC-08:00 Pacific', 'UTC-07:00 Mountain', 'UTC-06:00 Central',
'UTC-05:00 Eastern', 'UTC-03:00 Buenos Aires', 'UTC+00:00 London',
'UTC+01:00 Paris', 'UTC+02:00 Athens', 'UTC+03:00 Istanbul',
'UTC+04:00 Dubai', 'UTC+05:30 Mumbai', 'UTC+07:00 Bangkok',
'UTC+08:00 Singapore', 'UTC+09:00 Tokyo', 'UTC+10:00 Sydney',
'UTC+12:00 Auckland',
].map((label) => ({ label, value: label }));
export default function SelectScrollable() {
return (
<Select items={timezones}>
<SelectTrigger>
<SelectValue placeholder="Select a timezone" />
</SelectTrigger>
<SelectContent>
{timezones.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
Disabled options
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const plans = [
{ label: 'Free', value: 'free' },
{ label: 'Pro', value: 'pro' },
{ label: 'Enterprise (contact us)', value: 'enterprise', disabled: true },
];
export default function SelectDisabled() {
return (
<Select items={plans} defaultValue="pro">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{plans.map(({ label, value, disabled }) => (
<SelectItem key={value} value={value} disabled={disabled}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
Disabled
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const fruits = [{ label: 'Apple', value: 'apple' }];
export default function SelectComponentDisabled() {
return (
<Select items={fruits} disabled>
<SelectTrigger>
<SelectValue placeholder="Disabled" />
</SelectTrigger>
<SelectContent>
{fruits.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
Invalid
Wrap Select in a Field with invalid — the error propagates to the trigger automatically (aria-invalid/data-invalid), while FieldLabel and FieldError carry the visible error state.
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Field, FieldError, FieldLabel } from '@/components/ui/field';
const plans = [
{ label: 'Free', value: 'free' },
{ label: 'Pro', value: 'pro' },
{ label: 'Enterprise', value: 'enterprise' },
];
export default function SelectInvalid() {
return (
<Field invalid>
<FieldLabel htmlFor="select-invalid-plan">Plan</FieldLabel>
<Select items={plans}>
<SelectTrigger id="select-invalid-plan">
<SelectValue placeholder="Select a plan" />
</SelectTrigger>
<SelectContent>
{plans.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<FieldError>Please select a plan to continue.</FieldError>
</Field>
);
}
API reference
Built on Base UI Select. The tables below cover the props this library adds or changes — every other prop is forwarded to the underlying Base UI part; see the Base UI Select API reference for the full list.
Select (Root)
| Prop | Type | Default | Description |
|---|---|---|---|
items |
{ label: string; value: any }[] |
— | Item list; lets SelectValue render the selected label. |
value |
any |
— | Controlled selected value. |
defaultValue |
any |
— | |
onValueChange |
(value: any) => void |
— | |
disabled |
boolean |
false |
SelectContent
| Prop | Type | Default | Description |
|---|---|---|---|
alignItemWithTrigger |
boolean |
true |
Overlay the popup on the trigger with the selected item’s text aligned to it (macOS-style, with scroll arrows). Set false for a plain anchored dropdown below the trigger. |
side |
'top' | 'right' | 'bottom' | 'left' |
'bottom' |
Anchored mode only. |
sideOffset |
number |
4 |
Distance from the trigger, in px. |
align |
'start' | 'center' | 'end' |
'center' |
Anchored mode only. |
alignOffset |
number |
0 |
|
style |
StyleXStyles |
— | StyleX styles merged last — always win over the component’s own styles. |
SelectItem
| Prop | Type | Default | Description |
|---|---|---|---|
value (required) |
any |
— | |
disabled |
boolean |
false |
|
style |
StyleXStyles |
— | StyleX styles merged last — always win over the component’s own styles. |
Styling
SelectTrigger, SelectLabel, and SelectSeparator accept style (StyleXStyles, merged last so caller overrides always win) plus all native props of the element they render.
SelectValue and SelectGroup are Base UI parts re-exported unstyled.