Select
A select component that allows users to select a value from a list of options.
Preview
Loading...
import { useState } from "react";
import {
Select,
SelectTrigger,
SelectValue,
SelectIcon,
SelectPortal,
SelectContent,
SelectViewport,
SelectItem,
SelectItemText,
SelectItemIndicator,
SelectGroup,
SelectLabel,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
SelectArrow,
} from "@/components/ui/Select";
import { Label } from "@/components/ui/Label";
export function SelectDemo() {
return {
const [value, setValue] = useState("apple");
return (
<div className="space-y-1">
<Label>Choose a fruit</Label>
<Select value={value} onValueChange={setValue}>
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a fruit..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectScrollUpButton />
<SelectViewport>
<SelectItem value="apple">
<SelectItemText>Apple</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="banana">
<SelectItemText>Banana</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="orange">
<SelectItemText>Orange</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="grape">
<SelectItemText>Grape</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="mango">
<SelectItemText>Mango</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectViewport>
<SelectScrollDownButton />
<SelectArrow />
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};Installation
Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces
pnpm dlx shadcn@latest add @aura/selectManual
Install the following dependencies:
pnpm install @radix-ui/react-icons radix-uiCopy and paste the class names utility into your utils/class-names.ts file.
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Copy and paste the Select component into your components/ui/Select.tsx file.
"use client";
/**
* @description Displays a list of options for the user to pick from—triggered by a button.
*/
import * as React from "react";
import { Select as SelectRadix } from "radix-ui";
import {
CheckIcon,
ChevronDownIcon,
ChevronUpIcon,
} from "@radix-ui/react-icons";
import { cn } from "@/utils/class-names";
function Select({ ...props }: React.ComponentProps<typeof SelectRadix.Root>) {
return <SelectRadix.Root data-slot="select" {...props} />;
}
function SelectTrigger({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Trigger>) {
return (
<SelectRadix.Trigger
data-slot="select-trigger"
className={cn(
"flex h-4 w-full items-center justify-between rounded-sm border border-gray-a6 bg-gray-2 px-2 text-gray-12 cursor-pointer focus:outline-2 focus:outline-accent-9 focus:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
/>
);
}
function SelectValue({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Value>) {
return (
<SelectRadix.Value
data-slot="select-value"
className={cn(className, "text-gray-12")}
{...props}
/>
);
}
function SelectIcon({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Icon>) {
return (
<SelectRadix.Icon
data-slot="select-icon"
className={cn(className, "text-gray-11")}
{...props}
>
<ChevronDownIcon className="icon" />
</SelectRadix.Icon>
);
}
function SelectPortal({
...props
}: React.ComponentProps<typeof SelectRadix.Portal>) {
return <SelectRadix.Portal data-slot="select-portal" {...props} />;
}
function SelectContent({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Content>) {
return (
<SelectRadix.Content
data-slot="select-content"
collisionPadding={8}
className={cn(
className,
"relative z-50 max-h-96 min-w-10 overflow-hidden rounded-sm border border-gray-a6 bg-gray-1 shadow-md"
)}
{...props}
/>
);
}
function SelectViewport({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Viewport>) {
return (
<SelectRadix.Viewport
data-slot="select-viewport"
className={cn(className, "p-1")}
{...props}
/>
);
}
function SelectItem({
className,
children,
...props
}: React.ComponentProps<typeof SelectRadix.Item>) {
return (
<SelectRadix.Item
data-slot="select-item"
className={cn(
className,
"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1 px-2 text-gray-12 outline-none focus:bg-accent-3 focus:text-accent-12 data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
)}
{...props}
>
{children}
</SelectRadix.Item>
);
}
function SelectItemText({
className,
...props
}: React.ComponentProps<typeof SelectRadix.ItemText>) {
return (
<SelectRadix.ItemText
data-slot="select-item-text"
className={cn(className)}
{...props}
/>
);
}
function SelectItemIndicator({
className,
...props
}: React.ComponentProps<typeof SelectRadix.ItemIndicator>) {
return (
<SelectRadix.ItemIndicator
data-slot="select-item-indicator"
className={cn(
className,
"absolute left-0.5 top-0 bottom-0 flex items-center justify-center"
)}
{...props}
>
<CheckIcon className="size-1 text-accent-9" />
</SelectRadix.ItemIndicator>
);
}
function SelectGroup({
...props
}: React.ComponentProps<typeof SelectRadix.Group>) {
return <SelectRadix.Group data-slot="select-group" {...props} />;
}
function SelectLabel({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Label>) {
return (
<SelectRadix.Label
data-slot="select-label"
className={cn(className, "py-1 px-2 text-gray-12 font-medium")}
{...props}
/>
);
}
function SelectSeparator({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Separator>) {
return (
<SelectRadix.Separator
data-slot="select-separator"
className={cn(className, "m-0.6 h-px bg-gray-a6")}
{...props}
/>
);
}
function SelectScrollUpButton({
className,
...props
}: React.ComponentProps<typeof SelectRadix.ScrollUpButton>) {
return (
<SelectRadix.ScrollUpButton
data-slot="select-scroll-up-button"
className={cn(
className,
"flex cursor-default items-center justify-center py-1"
)}
{...props}
>
<ChevronUpIcon className="size-1 text-gray-11" />
</SelectRadix.ScrollUpButton>
);
}
function SelectScrollDownButton({
className,
...props
}: React.ComponentProps<typeof SelectRadix.ScrollDownButton>) {
return (
<SelectRadix.ScrollDownButton
data-slot="select-scroll-down-button"
className={cn(
className,
"flex cursor-default items-center justify-center py-1"
)}
{...props}
>
<ChevronDownIcon className="icon text-gray-11" />
</SelectRadix.ScrollDownButton>
);
}
function SelectArrow({
className,
...props
}: React.ComponentProps<typeof SelectRadix.Arrow>) {
return (
<SelectRadix.Arrow
data-slot="select-arrow"
className={cn(className, "fill-gray-1")}
{...props}
/>
);
}
export {
Select,
SelectPortal,
SelectTrigger,
SelectValue,
SelectIcon,
SelectContent,
SelectViewport,
SelectItem,
SelectItemText,
SelectItemIndicator,
SelectGroup,
SelectLabel,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
SelectArrow,
};Usage
WithDefaultValue
export const WithDefaultValue = () => {
return (
<div className="space-y-1">
<Label>Choose a fruit</Label>
<Select defaultValue="banana">
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a fruit..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectViewport>
<SelectItem value="apple">
<SelectItemText>Apple</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="banana">
<SelectItemText>Banana</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="orange">
<SelectItemText>Orange</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="grape">
<SelectItemText>Grape</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectViewport>
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};WithGroups
export const WithGroups = () => {
const [value, setValue] = useState("us");
return (
<div className="space-y-1">
<Label>Choose a country</Label>
<Select value={value} onValueChange={setValue}>
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a country..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectViewport>
<SelectGroup>
<SelectLabel>Americas</SelectLabel>
<SelectItem value="us">
<SelectItemText>United States</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="ca">
<SelectItemText>Canada</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="mx">
<SelectItemText>Mexico</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Europe</SelectLabel>
<SelectItem value="uk">
<SelectItemText>United Kingdom</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="fr">
<SelectItemText>France</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="de">
<SelectItemText>Germany</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Asia</SelectLabel>
<SelectItem value="jp">
<SelectItemText>Japan</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="cn">
<SelectItemText>China</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="kr">
<SelectItemText>South Korea</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectGroup>
</SelectViewport>
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};WithDisabledItems
export const WithDisabledItems = () => {
const [value, setValue] = useState("apple");
return (
<div className="space-y-1">
<Label>Choose a fruit</Label>
<Select value={value} onValueChange={setValue}>
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a fruit..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectViewport>
<SelectItem value="apple">
<SelectItemText>Apple</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="banana" disabled>
<SelectItemText>Banana (disabled)</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="orange">
<SelectItemText>Orange</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="grape" disabled>
<SelectItemText>Grape (disabled)</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="mango">
<SelectItemText>Mango</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectViewport>
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};Disabled
export const Disabled = () => {
return (
<div className="space-y-1">
<Label>Choose a fruit (disabled)</Label>
<Select disabled>
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a fruit..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectViewport>
<SelectItem value="apple">
<SelectItemText>Apple</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="banana">
<SelectItemText>Banana</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="orange">
<SelectItemText>Orange</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectViewport>
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};WithLongList
export const WithLongList = () => {
const [value, setValue] = useState("apple");
const fruits = [
"Apple",
"Banana",
"Orange",
"Pineapple",
"Grape",
"Mango",
"Strawberry",
"Blueberry",
"Raspberry",
"Blackberry",
"Cherry",
"Peach",
"Pear",
"Plum",
"Kiwi",
"Watermelon",
"Cantaloupe",
"Honeydew",
"Papaya",
"Guava",
];
return (
<div className="space-y-1">
<Label>Choose a fruit</Label>
<Select value={value} onValueChange={setValue}>
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a fruit..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectScrollUpButton />
<SelectViewport>
{fruits.map((fruit) => (
<SelectItem key={fruit} value={fruit.toLowerCase()}>
<SelectItemText>{fruit}</SelectItemText>
<SelectItemIndicator />
</SelectItem>
))}
</SelectViewport>
<SelectScrollDownButton />
<SelectArrow />
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};Complex
export const Complex = () => {
const [value, setValue] = useState("us");
return (
<div className="space-y-1">
<Label>Choose a location</Label>
<Select value={value} onValueChange={setValue}>
<SelectTrigger className="w-48">
<SelectValue placeholder="Select a location..." />
<SelectIcon />
</SelectTrigger>
<SelectPortal>
<SelectContent>
<SelectScrollUpButton />
<SelectViewport>
<SelectGroup>
<SelectLabel>North America</SelectLabel>
<SelectItem value="us">
<SelectItemText>United States</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="ca">
<SelectItemText>Canada</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="mx">
<SelectItemText>Mexico</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Europe</SelectLabel>
<SelectItem value="uk">
<SelectItemText>United Kingdom</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="fr">
<SelectItemText>France</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="de">
<SelectItemText>Germany</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="it">
<SelectItemText>Italy</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="es" disabled>
<SelectItemText>Spain (disabled)</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Asia Pacific</SelectLabel>
<SelectItem value="jp">
<SelectItemText>Japan</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="cn">
<SelectItemText>China</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="kr">
<SelectItemText>South Korea</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="in">
<SelectItemText>India</SelectItemText>
<SelectItemIndicator />
</SelectItem>
<SelectItem value="au">
<SelectItemText>Australia</SelectItemText>
<SelectItemIndicator />
</SelectItem>
</SelectGroup>
</SelectViewport>
<SelectScrollDownButton />
<SelectArrow />
</SelectContent>
</SelectPortal>
</Select>
</div>
);
};