A custom select with groups, scrolling, and validation, styled with the same tokens as Input and Native Select.
Looking for Native Select?
Prefer familiar browser behavior and a styled native HTML select? Use Native Select.
View Native Selectimport { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"const fruits = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" },]export function Example() { return ( <Select items={[...fruits]}> <SelectTrigger className="w-full"> <SelectValue placeholder="Select a fruit" /> </SelectTrigger> <SelectContent> <SelectGroup> {fruits.map((fruit) => ( <SelectItem key={fruit.value} value={fruit.value}> {fruit.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> )}Installation
npx @arctis-sh/@arctis-sh/ui@latest add selectUsage
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"const items = [ { label: "Light", value: "light" }, { label: "Dark", value: "dark" }, { label: "System", value: "system" },]<Select items={items}> <SelectTrigger className="w-[180px]"> <SelectValue placeholder="Theme" /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent></Select>Pass items to Select so SelectValue can resolve the label for the selected value.
Composition
Select
├── SelectTrigger
│ └── SelectValue
└── SelectContent
├── SelectGroup
│ ├── SelectLabel
│ ├── SelectItem
│ └── SelectItem
├── SelectSeparator
└── SelectGroup
├── SelectLabel
├── SelectItem
└── SelectItemBasic
A simple fruit picker with a placeholder.
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"const fruits = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" },]export function Example() { return ( <Select items={[...fruits]}> <SelectTrigger className="w-full"> <SelectValue placeholder="Select a fruit" /> </SelectTrigger> <SelectContent> <SelectGroup> {fruits.map((fruit) => ( <SelectItem key={fruit.value} value={fruit.value}> {fruit.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> )}Align Item With Trigger
Use alignItemWithTrigger on SelectContent to control how the selected item lines up with the trigger. At the default true, the popup places the selected item over the trigger. Set it to false to align the popup with the trigger edge.
Toggle to align the item with the trigger.
import { useState } from "react"import { Label } from "@/components/ui/label"import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"import { Switch } from "@/components/ui/switch"const fruits = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" },]export function Example() { const [alignItemWithTrigger, setAlignItemWithTrigger] = useState(true) return ( <div className="flex w-full flex-col gap-4"> <div className="flex items-center justify-between gap-3"> <Label htmlFor="align-item">Align Item</Label> <Switch id="align-item" checked={alignItemWithTrigger} onCheckedChange={setAlignItemWithTrigger} /> </div> <p className="text-sm tracking-wide text-muted-foreground"> Toggle to align the item with the trigger. </p> <Select defaultValue="banana" items={[...fruits]}> <SelectTrigger className="w-full"> <SelectValue /> </SelectTrigger> <SelectContent alignItemWithTrigger={alignItemWithTrigger}> <SelectGroup> {fruits.map((fruit) => ( <SelectItem key={fruit.value} value={fruit.value}> {fruit.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> </div> )}Groups
Use SelectGroup, SelectLabel, and SelectSeparator to organize items.
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@/components/ui/select"export function Example() { return ( <Select items={[ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" }, { label: "Carrot", value: "carrot" }, { label: "Broccoli", value: "broccoli" }, { label: "Spinach", value: "spinach" }, ]} > <SelectTrigger className="w-full"> <SelectValue placeholder="Select a fruit" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Fruits</SelectLabel> <SelectItem value="apple">Apple</SelectItem> <SelectItem value="banana">Banana</SelectItem> <SelectItem value="blueberry">Blueberry</SelectItem> <SelectItem value="grapes">Grapes</SelectItem> <SelectItem value="pineapple">Pineapple</SelectItem> </SelectGroup> <SelectSeparator /> <SelectGroup> <SelectLabel>Vegetables</SelectLabel> <SelectItem value="carrot">Carrot</SelectItem> <SelectItem value="broccoli">Broccoli</SelectItem> <SelectItem value="spinach">Spinach</SelectItem> </SelectGroup> </SelectContent> </Select> )}Scrollable
A long list scrolls inside SelectContent.
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue,} from "@/components/ui/select"const timezones = [ { label: "Eastern Standard Time (EST)", value: "est" }, { label: "Central Standard Time (CST)", value: "cst" }, { label: "Mountain Standard Time (MST)", value: "mst" }, { label: "Pacific Standard Time (PST)", value: "pst" }, { label: "Alaska Standard Time (AKST)", value: "akst" }, { label: "Hawaii Standard Time (HST)", value: "hst" }, { label: "Greenwich Mean Time (GMT)", value: "gmt" }, { label: "Central European Time (CET)", value: "cet" }, { label: "Eastern European Time (EET)", value: "eet" }, { label: "Japan Standard Time (JST)", value: "jst" }, { label: "Australia Eastern Time (AEST)", value: "aest" },]export function Example() { return ( <Select items={[...timezones]}> <SelectTrigger className="w-full"> <SelectValue placeholder="Select a timezone" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>North America</SelectLabel> <SelectItem value="est">Eastern Standard Time (EST)</SelectItem> <SelectItem value="cst">Central Standard Time (CST)</SelectItem> <SelectItem value="mst">Mountain Standard Time (MST)</SelectItem> <SelectItem value="pst">Pacific Standard Time (PST)</SelectItem> <SelectItem value="akst">Alaska Standard Time (AKST)</SelectItem> <SelectItem value="hst">Hawaii Standard Time (HST)</SelectItem> </SelectGroup> <SelectGroup> <SelectLabel>Europe & Asia</SelectLabel> <SelectItem value="gmt">Greenwich Mean Time (GMT)</SelectItem> <SelectItem value="cet">Central European Time (CET)</SelectItem> <SelectItem value="eet">Eastern European Time (EET)</SelectItem> <SelectItem value="jst">Japan Standard Time (JST)</SelectItem> <SelectItem value="aest">Australia Eastern Time (AEST)</SelectItem> </SelectGroup> </SelectContent> </Select> )}Disabled
Set disabled on Select to lock the control.
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"export function Example() { return ( <Select disabled items={[ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, ]}> <SelectTrigger className="w-full"> <SelectValue placeholder="Select a fruit" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectItem value="apple">Apple</SelectItem> <SelectItem value="banana">Banana</SelectItem> <SelectItem value="blueberry">Blueberry</SelectItem> </SelectGroup> </SelectContent> </Select> )}Invalid
Add data-invalid on Field and aria-invalid on SelectTrigger for an error state.
import { Field, FieldError, FieldLabel } from "@/components/ui/field"import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"export function Example() { return ( <Field data-invalid> <FieldLabel>Fruit</FieldLabel> <Select items={[ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, ]}> <SelectTrigger className="w-full" aria-invalid> <SelectValue placeholder="Select a fruit" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectItem value="apple">Apple</SelectItem> <SelectItem value="banana">Banana</SelectItem> <SelectItem value="blueberry">Blueberry</SelectItem> </SelectGroup> </SelectContent> </Select> <FieldError>Please select a fruit.</FieldError> </Field> )}<Field data-invalid> <FieldLabel>Fruit</FieldLabel> <SelectTrigger aria-invalid> <SelectValue /> </SelectTrigger> <FieldError>Please select a fruit.</FieldError></Field>API Reference
Select
Root. Built on Base UI Select. Pass items so value labels resolve in the trigger.
| Prop | Type | Default |
|---|---|---|
| items | { label: ReactNode, value: string }[] | — |
| value | string | — |
| defaultValue | string | — |
| onValueChange | (value: string) => void | — |
| disabled | boolean | false |
| open | boolean | — |
| defaultOpen | boolean | false |
| onOpenChange | (open: boolean) => void | — |
| name | string | — |
| required | boolean | false |
<Select items={items} defaultValue="apple"> <SelectTrigger /> <SelectContent /></Select>SelectTrigger
The button that opens the list.
| Prop | Type | Default |
|---|---|---|
| size | "default" | "sm" | "default" |
| className | string | — |
| aria-invalid | boolean | — |
<SelectTrigger className="w-[180px]"> <SelectValue placeholder="Theme" /></SelectTrigger>SelectValue
Shows the selected label, or the placeholder when empty.
| Prop | Type | Default |
|---|---|---|
| placeholder | string | — |
| className | string | — |
<SelectValue placeholder="Select a fruit" />SelectContent
The popup list. Positioning props pass through to the Base UI positioner.
| Prop | Type | Default |
|---|---|---|
| alignItemWithTrigger | boolean | true |
| side | "top" | "bottom" | "left" | "right" | "bottom" |
| align | "start" | "center" | "end" | "center" |
| sideOffset | number | 4 |
| alignOffset | number | 0 |
| className | string | — |
<SelectContent alignItemWithTrigger={false}> <SelectGroup> <SelectItem value="apple">Apple</SelectItem> </SelectGroup></SelectContent>SelectGroup
Groups related items.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
<SelectGroup> <SelectLabel>Fruits</SelectLabel> <SelectItem value="apple">Apple</SelectItem></SelectGroup>SelectLabel
Heading inside a group.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
<SelectLabel>Fruits</SelectLabel>SelectItem
A selectable option.
| Prop | Type | Default |
|---|---|---|
| value | string | — |
| disabled | boolean | false |
| className | string | — |
<SelectItem value="apple">Apple</SelectItem>SelectSeparator
Divider between groups.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
<SelectSeparator />