Skip to content
Esc
navigateopen⌘Jpreview
On this page

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.

Was this page helpful?