Select

A custom select with grouped, scrollable options.

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 Select
tsx
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>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add select

Usage

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
        └── SelectItem

Basic

A simple fruit picker with a placeholder.

tsx
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.

tsx
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.

tsx
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.

tsx
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.

tsx
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.

tsx
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.

PropTypeDefault
items{ label: ReactNode, value: string }[]
valuestring
defaultValuestring
onValueChange(value: string) => void
disabledbooleanfalse
openboolean
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void
namestring
requiredbooleanfalse
<Select items={items} defaultValue="apple">  <SelectTrigger />  <SelectContent /></Select>

SelectTrigger

The button that opens the list.

PropTypeDefault
size"default" | "sm""default"
classNamestring
aria-invalidboolean
<SelectTrigger className="w-[180px]">  <SelectValue placeholder="Theme" /></SelectTrigger>

SelectValue

Shows the selected label, or the placeholder when empty.

PropTypeDefault
placeholderstring
classNamestring
<SelectValue placeholder="Select a fruit" />

SelectContent

The popup list. Positioning props pass through to the Base UI positioner.

PropTypeDefault
alignItemWithTriggerbooleantrue
side"top" | "bottom" | "left" | "right""bottom"
align"start" | "center" | "end""center"
sideOffsetnumber4
alignOffsetnumber0
classNamestring
<SelectContent alignItemWithTrigger={false}>  <SelectGroup>    <SelectItem value="apple">Apple</SelectItem>  </SelectGroup></SelectContent>

SelectGroup

Groups related items.

PropTypeDefault
classNamestring
<SelectGroup>  <SelectLabel>Fruits</SelectLabel>  <SelectItem value="apple">Apple</SelectItem></SelectGroup>

SelectLabel

Heading inside a group.

PropTypeDefault
classNamestring
<SelectLabel>Fruits</SelectLabel>

SelectItem

A selectable option.

PropTypeDefault
valuestring
disabledbooleanfalse
classNamestring
<SelectItem value="apple">Apple</SelectItem>

SelectSeparator

Divider between groups.

PropTypeDefault
classNamestring
<SelectSeparator />