import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from "@registry/components/ui/select";
export function SelectExample() {
const items = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes" },
{ value: "pineapple", label: "Pineapple" },
];
return (
<div className="flex flex-wrap items-center gap-4">
<Select items={items}>
<SelectTrigger className="w-44">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
{items.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
} Examples
Trigger variants
The trigger supports the same variant and size props as Button.
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from "@registry/components/ui/select";
export function SelectVariantsExample() {
const variants = [
{ value: "outline", label: "Outline" },
{ value: "solid", label: "Solid" },
];
const sizes = [
{ value: "sm", label: "Small" },
{ value: "default", label: "Default" },
{ value: "lg", label: "Large" },
];
return (
<div className="flex flex-wrap items-center gap-4">
<Select items={variants} defaultValue="outline">
<SelectTrigger variant="outline" className="w-44">
<SelectValue placeholder="Variant" />
</SelectTrigger>
<SelectContent>
{variants.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Select items={sizes} defaultValue="sm">
<SelectTrigger variant="outline" size="sm" className="w-44">
<SelectValue placeholder="Size" />
</SelectTrigger>
<SelectContent>
{sizes.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
} With separator
Use SelectSeparator to visually group related options.
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue, SelectSeparator } from "@registry/components/ui/select";
export function SelectWithGroupsExample() {
const items = [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "svelte", label: "Svelte" },
{ value: "next", label: "Next.js" },
{ value: "nuxt", label: "Nuxt" },
{ value: "astro", label: "Astro" },
];
return (
<div className="flex flex-wrap items-center gap-4">
<Select items={items} defaultValue="react">
<SelectTrigger variant="outline" className="w-56">
<SelectValue placeholder="Select a framework" />
</SelectTrigger>
<SelectContent>
<SelectItem value="react">React</SelectItem>
<SelectItem value="vue">Vue</SelectItem>
<SelectItem value="svelte">Svelte</SelectItem>
<SelectSeparator />
<SelectItem value="next">Next.js</SelectItem>
<SelectItem value="nuxt">Nuxt</SelectItem>
<SelectItem value="astro">Astro</SelectItem>
</SelectContent>
</Select>
</div>
);
} Controlled
Manage the selected value with React state.
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from "@registry/components/ui/select";
import { useState } from "react";
export function SelectControlledExample() {
const items = [
{ value: "typescript", label: "TypeScript" },
{ value: "python", label: "Python" },
{ value: "go", label: "Go" },
{ value: "rust", label: "Rust" },
];
const [value, setValue] = useState("");
return (
<div className="flex flex-col gap-3">
<Select items={items} value={value} onValueChange={(v) => setValue(v ?? "")}>
<SelectTrigger variant="outline" className="w-44">
<SelectValue placeholder="Pick a language" />
</SelectTrigger>
<SelectContent>
{items.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
{value && <p className="text-sm text-gray-500">Selected: {value}</p>}
</div>
);
} With items prop
Pass an items array to Select so SelectValue renders the matching label instead of the raw value.
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from "@registry/components/ui/select";
export function SelectWithItemsExample() {
const items = [
{ value: "utc", label: "UTC" },
{ value: "est", label: "Eastern Time" },
{ value: "cst", label: "Central Time" },
{ value: "mst", label: "Mountain Time" },
{ value: "pst", label: "Pacific Time" },
{ value: "jst", label: "Japan Standard Time" },
];
return (
<div className="flex flex-wrap items-center gap-4">
<Select items={items} defaultValue="utc">
<SelectTrigger variant="outline" className="w-56">
<SelectValue placeholder="Select time zone" />
</SelectTrigger>
<SelectContent>
{items.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
} Installation
Copy the source code below into your project:
import { Select as BaseSelect } from "@base-ui/react/select";
import { cn } from "@registry/lib/utils";
import { CaretDownIcon, CheckIcon } from "@phosphor-icons/react";
import type { VariantProps } from "class-variance-authority";
import { buttonVariants } from "./button";
function Select<T>(props: BaseSelect.Root.Props<T>) {
return <BaseSelect.Root data-slot="select" {...props} />;
}
function SelectTrigger({
children,
className,
variant = "outline",
size = "default",
...props
}: BaseSelect.Trigger.Props & VariantProps<typeof buttonVariants>) {
return (
<BaseSelect.Trigger
className={(state) =>
cn(
buttonVariants({ className, size, variant }),
"justify-between gap-3 [&_svg]:transition-transform [&_svg]:duration-150",
state.open && "[&_svg]:rotate-180",
className,
)
}
data-slot="select-trigger"
{...props}
>
{children}
<BaseSelect.Icon render={<CaretDownIcon className="size-4" />} />
</BaseSelect.Trigger>
);
}
function SelectValue(props: BaseSelect.Value.Props) {
return <BaseSelect.Value data-slot="select-value" {...props} />;
}
function SelectContent({
className,
positionerProps,
listProps,
children,
...props
}: BaseSelect.Popup.Props & {
positionerProps?: BaseSelect.Positioner.Props;
listProps?: BaseSelect.List.Props;
}) {
return (
<BaseSelect.Portal>
<BaseSelect.Positioner
{...positionerProps}
alignItemWithTrigger={positionerProps?.alignItemWithTrigger ?? false}
sideOffset={8}
className={cn(
"z-10 min-w-(--anchor-width) outline-none select-none",
positionerProps?.className,
)}
>
<BaseSelect.Popup
className={cn(
"group origin-(--transform-origin) rounded-xl bg-popover bg-clip-padding text-popover-foreground shadow-lg outline outline-border transition-[transform,scale,opacity] data-ending-style:scale-90 data-ending-style:opacity-0 data-starting-style:scale-90 data-starting-style:opacity-0 data-[side=none]:data-ending-style:transition-none data-[side=none]:data-starting-style:scale-100 data-[side=none]:data-starting-style:opacity-100 data-[side=none]:data-starting-style:transition-none dark:shadow-none",
className,
)}
data-slot="select-popup"
{...props}
>
<BaseSelect.List
className="relative grid max-h-(--available-height) scroll-py-6 grid-cols-[auto_1fr] overflow-y-auto p-1.5"
data-slot="select-list"
{...listProps}
>
{children}
</BaseSelect.List>
</BaseSelect.Popup>
</BaseSelect.Positioner>
</BaseSelect.Portal>
);
}
function SelectItem({ className, children, ...props }: BaseSelect.Item.Props) {
return (
<BaseSelect.Item
className={cn(
"group/selectitem col-span-full grid min-w-(--anchor-width) cursor-default grid-cols-subgrid items-center gap-2 py-2 pr-4 pl-2.5 text-sm leading-4 outline-none select-none group-data-[side=none]:min-w-[calc(var(--anchor-width)+1rem)] group-data-[side=none]:pr-12 group-data-[side=none]:text-base group-data-[side=none]:leading-4 data-highlighted:relative data-highlighted:z-0 data-highlighted:text-primary-foreground data-highlighted:before:absolute data-highlighted:before:inset-0 data-highlighted:before:z-[-1] data-highlighted:before:rounded-lg data-highlighted:before:bg-primary pointer-coarse:py-2.5 pointer-coarse:text-[0.925rem]",
className,
)}
data-slot="select-item"
{...props}
>
<SelectItemIndicator />
<BaseSelect.ItemText className="col-start-2" data-slot="select-item-text">
{children}
</BaseSelect.ItemText>
</BaseSelect.Item>
);
}
function SelectItemIndicator({ className, ...props }: BaseSelect.ItemIndicator.Props) {
return (
<div className="col-start-1 size-4">
<BaseSelect.ItemIndicator
className={className}
data-slot="select-item-indicator"
render={<CheckIcon />}
{...props}
/>
</div>
);
}
function SelectSeparator(props: BaseSelect.Separator.Props) {
return (
<BaseSelect.Separator
className="col-span-full my-1.5 h-px bg-border"
data-slot="select-separator"
{...props}
/>
);
}
export { Select, SelectTrigger, SelectContent, SelectItem, SelectValue, SelectSeparator }; API Reference
Select
This component does not add any props on top of Base UI Select.Root . See the Base UI docs for the full API reference.
SelectTrigger
The SelectTrigger component extends the Base UI Select.Trigger
props and adds the following:
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "secondary" | "outline" | "ghost" | "destructive" | "revert" | "outline" | Visual style of the trigger button. |
| size | "default" | "sm" | "lg" | "icon" | "icon-sm" | "default" | Size of the trigger button. |
SelectContent
The SelectContent component extends the Base UI Select.Popup
props and adds the following:
| Prop | Type | Default | Description |
|---|---|---|---|
| positionerProps | BaseSelect.Positioner.Props | — | Props forwarded to the underlying Positioner component. Includes align, side, sideOffset, alignOffset, collisionPadding, etc. |
| listProps | BaseSelect.List.Props | — | Props forwarded to the underlying List component. |
SelectItem
This component does not add any props on top of Base UI Select.Item . See the Base UI docs for the full API reference.
SelectValue
This component does not add any props on top of Base UI Select.Value . See the Base UI docs for the full API reference.
SelectSeparator
This component does not add any props on top of Base UI Select.Separator . See the Base UI docs for the full API reference.