Inputs
Select
Choose one option from a short list in a popup, styled to match the rest of the form.
npx prodular-ui add selectWith a field
New teammates join with this role.
"use client"
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"
const roles = [ { label: "Admin", value: "admin" }, { label: "Member", value: "member" }, { label: "Viewer", value: "viewer" }, { label: "Billing only", value: "billing" },]
export default function SelectDefault() { return ( <Field className="max-w-xs"> <FieldLabel>Default role</FieldLabel> <Select items={roles} defaultValue="member"> <SelectTrigger className="w-full"> <SelectValue /> </SelectTrigger> <SelectContent> {roles.map((role) => ( <SelectItem key={role.value} value={role.value}> {role.label} </SelectItem> ))} </SelectContent> </Select> <FieldDescription>New teammates join with this role.</FieldDescription> </Field> )}Groups and icons
Group long lists with SelectLabel and a separator.
"use client"
import { GlobeIcon } from "lucide-react"
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@/components/ui/select"
const regions = [ { label: "Americas", zones: [ { label: "Pacific Time (GMT-8)", value: "America/Los_Angeles" }, { label: "Eastern Time (GMT-5)", value: "America/New_York" }, { label: "Sao Paulo (GMT-3)", value: "America/Sao_Paulo" }, ], }, { label: "Europe", zones: [ { label: "London (GMT+0)", value: "Europe/London" }, { label: "Berlin (GMT+1)", value: "Europe/Berlin" }, ], }, { label: "Asia Pacific", zones: [ { label: "Singapore (GMT+8)", value: "Asia/Singapore" }, { label: "Tokyo (GMT+9)", value: "Asia/Tokyo" }, ], },]
const items = regions.flatMap((r) => r.zones)
export default function SelectGroups() { return ( <Select items={items}> <SelectTrigger className="w-64" aria-label="Timezone"> <GlobeIcon className="text-muted-foreground" /> <SelectValue placeholder="Choose a timezone" /> </SelectTrigger> <SelectContent alignItemWithTrigger={false}> {regions.map((region, i) => ( <SelectGroup key={region.label}> {i > 0 && <SelectSeparator />} <SelectLabel>{region.label}</SelectLabel> {region.zones.map((zone) => ( <SelectItem key={zone.value} value={zone.value}> {zone.label} </SelectItem> ))} </SelectGroup> ))} </SelectContent> </Select> )}Sizes
"use client"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"
const ranges = [ { label: "Last 7 days", value: "7d" }, { label: "Last 30 days", value: "30d" }, { label: "This quarter", value: "qtd" },]
export default function SelectSizes() { return ( <div className="flex flex-wrap items-center justify-center gap-3"> {(["sm", "default", "lg"] as const).map((size) => ( <Select key={size} items={ranges} defaultValue="30d"> <SelectTrigger size={size} aria-label="Date range"> <SelectValue /> </SelectTrigger> <SelectContent> {ranges.map((r) => ( <SelectItem key={r.value} value={r.value}> {r.label} </SelectItem> ))} </SelectContent> </Select> ))} </div> )}Anatomy
<Select items={roles} defaultValue="member"> <SelectTrigger className="w-full"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectItem value="member">Member</SelectItem> </SelectContent></Select>Guidance
Use it when
- Five to fifteen known options where only one can apply: role, timezone, status, date range.
Avoid
- Two or three options that people should compare. Use RadioGroup.
- Long or searchable lists such as people or countries. Use Combobox.
- Mobile-first forms where the system picker is better. Use NativeSelect.
Rules for AI agents
Published in llms-full.txt and the registry, so coding agents follow them when they build with this component.
- Pass the options to Select as items={[{ label, value }]} so SelectValue shows the label, not the raw value.
- Use SelectValue placeholder for the empty state. Do not add a fake first option.
- Give the trigger a width (w-full inside forms, a fixed width in toolbars) so it does not jump when the value changes.
- Inside Field the label is linked automatically. Outside Field, add aria-label to SelectTrigger.
- Match SelectTrigger size to nearby Buttons and Inputs.
- Use size="xs" on SelectTrigger for dense toolbars and inspectors instead of overriding its height.
API
Select
| Prop | Type | Default | Description |
|---|---|---|---|
| items | { label: ReactNode; value: unknown }[] | Options used to render the selected label. | |
| value / defaultValue | unknown | Controlled or initial value. | |
| onValueChange | (value) => void | Called when the selection changes. |
SelectTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "xs" | "sm" | "default" | "lg" | "default" | Height. Matches Button and Input. |
SelectContent
| Prop | Type | Default | Description |
|---|---|---|---|
| alignItemWithTrigger | boolean | true | Overlays the selected item on the trigger. Set false to open below it. |