Select
A common form component for choosing a predefined value in a dropdown menu. Built with Base UI and Tailwind CSS. Copy-paste ready.
export default function Component() {
return <div>Component</div>;
}
Installation
pnpm dlx shadcn@latest add @cnippet/select
Usage
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"const items = [
{ label: "Select framework", value: null },
{ label: "Next.js", value: "next" },
{ label: "Vite", value: "vite" },
{ label: "Astro", value: "astro" },
]
<Select items={items}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectPopup>
{items.map((item) => (
<SelectItem key={item.value} value={item}>
{item.label}
</SelectItem>
))}
</SelectPopup>
</Select>Examples
For accessible labelling and validation, prefer using the Field component to wrap selects. See the related example: Select field.
Sizes
Renders the sm, default, and lg size variants to show how the select trigger scales across layout contexts.
export default function Component() {
return <div>Component</div>;
}
With Groups
Organizes options into labeled SelectGroup sections for categorized lists such as timezones or regions.
export default function Component() {
return <div>Component</div>;
}
Multiple Selection
Allows selecting multiple values simultaneously, displaying them as a comma-separated list in the trigger.
export default function Component() {
return <div>Component</div>;
}
Options with Icon
Prepends an icon to each option in the dropdown for visual identification of the available choices.
export default function Component() {
return <div>Component</div>;
}
With Object Values
Demonstrates using complex object values (e.g. { id, label }) rather than plain strings, letting you store structured data on selection.
export default function Component() {
return <div>Component</div>;
}
Form Integration
Wraps the select in Form and Field for required validation and a FieldError displayed on submit.
export default function Component() {
return <div>Component</div>;
}
With Disabled Options
Active role options are listed first; unavailable roles (Guest, Contractor) appear below a separator as disabled items with a "coming soon" label.
import {
Select,
SelectItem,
SelectPopup,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const activeRoles = [
{ label: "Owner", value: "owner" },
{ label: "Admin", value: "admin" },
{ label: "Editor", value: "editor" },
{ label: "Viewer", value: "viewer" },
];
const comingSoon = [
{ label: "Guest", value: "guest" },
{ label: "Contractor", value: "contractor" },
];
const placeholder = { label: "Select a role", value: null };
const allItems = [placeholder, ...activeRoles, ...comingSoon];
export default function Particle() {
return (
<Select defaultValue={activeRoles[2]} items={allItems}>
<SelectTrigger className="w-52">
<SelectValue />
</SelectTrigger>
<SelectPopup>
{activeRoles.map((item) => (
<SelectItem key={item.value} value={item}>
{item.label}
</SelectItem>
))}
<SelectSeparator />
{comingSoon.map((item) => (
<SelectItem disabled key={item.value} value={item}>
{item.label}
<span className="ms-1.5 text-muted-foreground text-xs">
(coming soon)
</span>
</SelectItem>
))}
</SelectPopup>
</Select>
);
}
Country Selector with Groups
Options organized into SelectGroup regions (Americas, Europe, Asia Pacific) with SelectGroupLabel headings for each continent.
import {
Select,
SelectGroup,
SelectGroupLabel,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const regionGroups = [
{
items: [
{ label: "United States", value: "us" },
{ label: "Canada", value: "ca" },
{ label: "Brazil", value: "br" },
],
label: "Americas",
},
{
items: [
{ label: "United Kingdom", value: "gb" },
{ label: "Germany", value: "de" },
{ label: "France", value: "fr" },
],
label: "Europe",
},
{
items: [
{ label: "Japan", value: "jp" },
{ label: "Australia", value: "au" },
{ label: "India", value: "in" },
],
label: "Asia Pacific",
},
];
const placeholder = { label: "Select a region", value: null };
const allItems = [placeholder, ...regionGroups.flatMap((g) => g.items)];
export default function Particle() {
return (
<Select items={allItems}>
<SelectTrigger className="w-52">
<SelectValue />
</SelectTrigger>
<SelectPopup>
{regionGroups.map((group) => (
<SelectGroup key={group.label}>
<SelectGroupLabel>{group.label}</SelectGroupLabel>
{group.items.map((item) => (
<SelectItem key={item.value} value={item}>
{item.label}
</SelectItem>
))}
</SelectGroup>
))}
</SelectPopup>
</Select>
);
}
Controlled with External Display
A priority selector whose selected value drives a Badge rendered below the trigger — shows how to wire onValueChange to external UI.
Priority: not set
"use client";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
type PriorityItem = { label: string; value: string | null };
const priorities: PriorityItem[] = [
{ label: "Select priority", value: null },
{ label: "Critical", value: "critical" },
{ label: "High", value: "high" },
{ label: "Medium", value: "medium" },
{ label: "Low", value: "low" },
];
const badgeVariant: Record<string, "destructive" | "secondary" | "outline"> = {
critical: "destructive",
high: "destructive",
low: "outline",
medium: "secondary",
};
export default function Particle() {
const [selected, setSelected] = useState<PriorityItem | null>(null);
return (
<div className="flex w-56 flex-col gap-3">
<Select
items={priorities}
onValueChange={(v) => setSelected(v as PriorityItem)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectPopup>
{priorities.slice(1).map((item) => (
<SelectItem key={item.value} value={item}>
{item.label}
</SelectItem>
))}
</SelectPopup>
</Select>
<p className="text-muted-foreground text-sm">
Priority:{" "}
{selected?.value ? (
<Badge
className="ms-1"
variant={badgeVariant[selected.value] ?? "outline"}
>
{selected.label}
</Badge>
) : (
<span>not set</span>
)}
</p>
</div>
);
}
Items with Description
Each dropdown option includes a secondary description line below its label — useful for environment selectors, plan tiers, or deployment targets.
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const deployTargets: {
description: string;
label: string;
value: string | null;
}[] = [
{ description: "", label: "Select environment", value: null },
{
description: "Public-facing production environment",
label: "Production",
value: "production",
},
{
description: "Internal testing and QA environment",
label: "Staging",
value: "staging",
},
{
description: "For feature development and experiments",
label: "Development",
value: "development",
},
{
description: "Isolated per-branch preview deployments",
label: "Preview",
value: "preview",
},
];
export default function Particle() {
return (
<Select defaultValue={deployTargets[2]} items={deployTargets}>
<SelectTrigger className="w-56">
<SelectValue />
</SelectTrigger>
<SelectPopup>
{deployTargets.slice(1).map((item) => (
<SelectItem key={item.value} value={item}>
<span className="flex flex-col gap-0.5">
<span>{item.label}</span>
<span className="text-muted-foreground text-xs">
{item.description}
</span>
</span>
</SelectItem>
))}
</SelectPopup>
</Select>
);
}
Sort Order with Icons
A compact sort select where each option is prefixed with a lucide-react icon representing the sort direction or criteria.
"use client";
import type { LucideIcon } from "lucide-react";
import {
ArrowDownAZIcon,
ArrowUpAZIcon,
CalendarArrowDownIcon,
CalendarArrowUpIcon,
SparklesIcon,
} from "lucide-react";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
type SortItem = { Icon: LucideIcon; label: string; value: string };
const sortOptions: SortItem[] = [
{ Icon: SparklesIcon, label: "Relevance", value: "relevance" },
{ Icon: ArrowDownAZIcon, label: "Name A–Z", value: "name-asc" },
{ Icon: ArrowUpAZIcon, label: "Name Z–A", value: "name-desc" },
{ Icon: CalendarArrowDownIcon, label: "Newest first", value: "date-desc" },
{ Icon: CalendarArrowUpIcon, label: "Oldest first", value: "date-asc" },
];
const placeholder = { label: "Sort by", value: null };
const allItems = [placeholder, ...sortOptions];
export default function Particle() {
return (
<Select defaultValue={sortOptions[0]} items={allItems}>
<SelectTrigger className="w-44">
<SelectValue />
</SelectTrigger>
<SelectPopup>
{sortOptions.map((item) => {
const { Icon, label, value } = item;
return (
<SelectItem key={value} value={item}>
<span className="flex items-center gap-2">
<Icon aria-hidden="true" className="size-4" />
{label}
</span>
</SelectItem>
);
})}
</SelectPopup>
</Select>
);
}

