1import {2 Select,3 SelectContent,4 SelectGroup,5 SelectItem,6 SelectLabel,7 SelectTrigger,8 SelectValue,9} from "@/components/ui/pxl/select";10
11const items = [12 { label: "Select a fruit", value: null },13 { label: "Apple", value: "apple" },14 { label: "Banana", value: "banana" },15 { label: "Blueberry", value: "blueberry" },16 { label: "Grapes", value: "grapes" },17]18
19export default function SelectDemo() {20 return (21 <Select items={items}>22 <SelectTrigger className="w-full max-w-48">23 <SelectValue />24 </SelectTrigger>25 <SelectContent>26 <SelectGroup>27 <SelectLabel>Fruits</SelectLabel>28 {items.map((item) => (29 <SelectItem key={item.value} value={item.value}>30 {item.label}31 </SelectItem>32 ))}33 </SelectGroup>34 </SelectContent>35 </Select>36 );37}Installation
Section titled “Installation”pnpm dlx shadcn@latest add pxl-ui/registry/select
Install the following dependencies:
pnpm add @base-ui/react
Copy and paste the following code into your project.
1"use client";2
3import { Select as SelectPrimitive } from "@base-ui/react/select";4import { cva, type VariantProps } from "class-variance-authority";5import type { ComponentProps } from "react";6
7import { cn } from "@/lib/utils";8
9const Select = SelectPrimitive.Root;10
11const selectVariants = cva(12 "flex w-fit items-center justify-between gap-1.5 py-2 pr-2 pl-2.5 whitespace-nowrap transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 data-placeholder:text-muted-foreground *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",13 {14 variants: {15 border: {16 solid: "pixel-border",17 none: "",18 },19 size: {20 default: "h-9 text-sm",21 sm: "h-8 text-sm",22 },23 },24 defaultVariants: {25 border: "solid",26 size: "default",27 },28 },29);30
31function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {32 return (33 <SelectPrimitive.Group34 data-slot="select-group"35 className={cn("scroll-my-1 p-1", className)}36 {...props}37 />38 );39}40
41function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {42 return (43 <SelectPrimitive.Value44 data-slot="select-value"45 className={cn("flex flex-1 text-left", className)}46 {...props}47 />48 );49}50
51function SelectTrigger({52 className,53 border = "solid",54 size = "default",55 children,56 ...props57}: SelectPrimitive.Trigger.Props & VariantProps<typeof selectVariants>) {58 return (59 <SelectPrimitive.Trigger60 data-slot="select-trigger"61 data-size={size}62 className={cn(63 selectVariants({64 border,65 size,66 }),67 className,68 )}69 {...props}70 >71 {children}72 <SelectPrimitive.Icon73 render={74 <svg75 className="pointer-events-none size-4 text-muted-foreground"76 xmlns="http://www.w3.org/2000/svg"77 fill="currentColor"78 viewBox="0 0 24 24"79 >80 <path d="M13 16h-2v-2h2v2Zm-2-2H9v-2h2v2Zm4 0h-2v-2h2v2Zm-6-2H7v-2h2v2Zm8 0h-2v-2h2v2ZM7 10H5V8h2v2Zm12 0h-2V8h2v2Z" />81 </svg>82 }83 />84 </SelectPrimitive.Trigger>85 );86}87
88function SelectContent({89 className,90 children,91 side = "bottom",92 sideOffset = 4,93 align = "center",94 alignOffset = 0,95 alignItemWithTrigger = true,96 ...props97}: SelectPrimitive.Popup.Props &98 Pick<99 SelectPrimitive.Positioner.Props,100 "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"101 >) {102 return (103 <SelectPrimitive.Portal>104 <SelectPrimitive.Positioner105 side={side}106 sideOffset={sideOffset}107 align={align}108 alignOffset={alignOffset}109 alignItemWithTrigger={alignItemWithTrigger}110 className="isolate z-50"111 >112 <SelectPrimitive.Popup113 data-slot="select-content"114 data-align-trigger={alignItemWithTrigger}115 className={cn(116 "relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto pixel-border pixel-size-[5px] pixel-color-[color-mix(in_oklab,var(--foreground)_10%,transparent)] bg-popover p-1.5 text-popover-foreground duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",117 className,118 )}119 {...props}120 >121 <SelectScrollUpButton />122 <SelectPrimitive.List>{children}</SelectPrimitive.List>123 <SelectScrollDownButton />124 </SelectPrimitive.Popup>125 </SelectPrimitive.Positioner>126 </SelectPrimitive.Portal>127 );128}129
130function SelectLabel({131 className,132 ...props133}: SelectPrimitive.GroupLabel.Props) {134 return (135 <SelectPrimitive.GroupLabel136 data-slot="select-label"137 className={cn(138 "px-2 py-1.5 text-xs font-heading text-muted-foreground",139 className,140 )}141 {...props}142 />143 );144}145
146function SelectItem({147 className,148 children,149 ...props150}: SelectPrimitive.Item.Props) {151 return (152 <SelectPrimitive.Item153 data-slot="select-item"154 className={cn(155 "relative flex w-full cursor-default items-center gap-2 py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",156 className,157 )}158 {...props}159 >160 <SelectPrimitive.ItemText className="flex flex-1 shrink-0 gap-2 whitespace-nowrap">161 {children}162 </SelectPrimitive.ItemText>163 <SelectPrimitive.ItemIndicator164 render={165 <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />166 }167 >168 <svg169 className="pointer-events-none"170 xmlns="http://www.w3.org/2000/svg"171 fill="currentColor"172 viewBox="0 0 24 24"173 >174 <path d="M10 18H8v-2h2v2Zm-2-2H6v-2h2v2Zm4-2v2h-2v-2h2Zm-6 0H4v-2h2v2Zm8 0h-2v-2h2v2Zm2-2h-2v-2h2v2Zm2-2h-2V8h2v2Zm2-2h-2V6h2v2Z" />175 </svg>176 </SelectPrimitive.ItemIndicator>177 </SelectPrimitive.Item>178 );179}180
181function SelectSeparator({182 className,183 ...props184}: SelectPrimitive.Separator.Props) {185 return (186 <SelectPrimitive.Separator187 data-slot="select-separator"188 className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}189 {...props}190 />191 );192}193
194function SelectScrollUpButton({195 className,196 ...props197}: ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {198 return (199 <SelectPrimitive.ScrollUpArrow200 data-slot="select-scroll-up-button"201 className={cn(202 "top-1.5 z-10 flex w-[calc(100%-(--spacing(3)))] cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",203 className,204 )}205 {...props}206 >207 <svg208 xmlns="http://www.w3.org/2000/svg"209 fill="currentColor"210 viewBox="0 0 24 24"211 >212 <path d="M13 8h-2v2h2V8Zm-2 2H9v2h2v-2Zm4 0h-2v2h2v-2Zm-6 2H7v2h2v-2Zm8 0h-2v2h2v-2ZM7 14H5v2h2v-2Zm12 0h-2v2h2v-2Z" />213 </svg>214 </SelectPrimitive.ScrollUpArrow>215 );216}217
218function SelectScrollDownButton({219 className,220 ...props221}: ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {222 return (223 <SelectPrimitive.ScrollDownArrow224 data-slot="select-scroll-down-button"225 className={cn(226 "bottom-1.5 z-10 flex w-[calc(100%-(--spacing(3)))] cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",227 className,228 )}229 {...props}230 >231 <svg232 xmlns="http://www.w3.org/2000/svg"233 fill="currentColor"234 viewBox="0 0 24 24"235 >236 <path d="M13 16h-2v-2h2v2Zm-2-2H9v-2h2v2Zm4 0h-2v-2h2v2Zm-6-2H7v-2h2v2Zm8 0h-2v-2h2v2ZM7 10H5V8h2v2Zm12 0h-2V8h2v2Z" />237 </svg>238 </SelectPrimitive.ScrollDownArrow>239 );240}241
242export {243 Select,244 SelectContent,245 SelectGroup,246 SelectItem,247 SelectLabel,248 SelectScrollDownButton,249 SelectScrollUpButton,250 SelectSeparator,251 SelectTrigger,252 SelectValue,253};Update the import paths to match your project setup.
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/pxl/select"const items = [ { label: "Light", value: "light" }, { label: "Dark", value: "dark" }, { label: "System", value: "system" },]
<Select items={items}> <SelectTrigger className="w-45"> <SelectValue placeholder="Theme" /> </SelectTrigger> <SelectContent> <SelectGroup> {items.map((item) => ( <SelectItem key={item.value} value={item.value}> {item.label} </SelectItem> ))} </SelectGroup> </SelectContent></Select>Composition
Section titled “Composition”Use the following composition to build a Select:
Select├── SelectTrigger│ └── SelectValue└── SelectContent ├── SelectGroup │ ├── SelectLabel │ ├── SelectItem │ └── SelectItem ├── SelectSeparator └── SelectGroup ├── SelectLabel ├── SelectItem └── SelectItemAlign Item With Trigger
Section titled “Align Item With Trigger”Use alignItemWithTrigger on SelectContent to control whether the selected item aligns with the trigger. When true (default), the popup positions so the selected item appears over the trigger. When false, the popup aligns to the trigger edge.
Toggle to align the item with the trigger.
1"use client";2
3import * as React from "react";4
5import {6 Field,7 FieldContent,8 FieldDescription,9 FieldGroup,10 FieldLabel,11} from "@/components/ui/pxl/field";12import {13 Select,14 SelectContent,15 SelectGroup,16 SelectItem,17 SelectTrigger,18 SelectValue,19} from "@/components/ui/pxl/select";20import { Switch } from "@/components/ui/pxl/switch";21
22const items = [23 { label: "Select a fruit", value: null },24 { label: "Apple", value: "apple" },25 { label: "Banana", value: "banana" },26 { label: "Blueberry", value: "blueberry" },27 { label: "Grapes", value: "grapes" },28 { label: "Pineapple", value: "pineapple" },29];30
31export default function SelectAlignItemExample() {32 const [alignItemWithTrigger, setAlignItemWithTrigger] = React.useState(true);33
34 return (35 <FieldGroup className="w-full max-w-xs">36 <Field orientation="horizontal">37 <FieldContent>38 <FieldLabel htmlFor="align-item">Align Item</FieldLabel>39 <FieldDescription>40 Toggle to align the item with the trigger.41 </FieldDescription>42 </FieldContent>43 <Switch44 id="align-item"45 checked={alignItemWithTrigger}46 onCheckedChange={setAlignItemWithTrigger}47 />48 </Field>49 <Field>50 <Select items={items} defaultValue="banana">51 <SelectTrigger>52 <SelectValue />53 </SelectTrigger>54 <SelectContent alignItemWithTrigger={alignItemWithTrigger}>55 <SelectGroup>56 {items.map((item) => (57 <SelectItem key={item.value} value={item.value}>58 {item.label}59 </SelectItem>60 ))}61 </SelectGroup>62 </SelectContent>63 </Select>64 </Field>65 </FieldGroup>66 );67}Groups
Section titled “Groups”Use SelectGroup, SelectLabel, and SelectSeparator to organize items.
1import {2 Select,3 SelectContent,4 SelectGroup,5 SelectItem,6 SelectLabel,7 SelectSeparator,8 SelectTrigger,9 SelectValue,10} from "@/components/ui/pxl/select"11
12export default function SelectGroupsExample() {13 const fruits = [14 { label: "Apple", value: "apple" },15 { label: "Banana", value: "banana" },16 { label: "Blueberry", value: "blueberry" },17 ]18 const vegetables = [19 { label: "Carrot", value: "carrot" },20 { label: "Broccoli", value: "broccoli" },21 { label: "Spinach", value: "spinach" },22 ]23 const allItems = [24 { label: "Select a fruit", value: null },25 ...fruits,26 ...vegetables,27 ]28 return (29 <Select items={allItems}>30 <SelectTrigger className="w-full max-w-48">31 <SelectValue />32 </SelectTrigger>33 <SelectContent>34 <SelectGroup>35 <SelectLabel>Fruits</SelectLabel>36 {fruits.map((item) => (37 <SelectItem key={item.value} value={item.value}>38 {item.label}39 </SelectItem>40 ))}41 </SelectGroup>42 <SelectSeparator />43 <SelectGroup>44 <SelectLabel>Vegetables</SelectLabel>45 {vegetables.map((item) => (46 <SelectItem key={item.value} value={item.value}>47 {item.label}48 </SelectItem>49 ))}50 </SelectGroup>51 </SelectContent>52 </Select>53 )54}Scrollable
Section titled “Scrollable”A select with many items that scrolls.
1import {2 Select,3 SelectContent,4 SelectGroup,5 SelectItem,6 SelectLabel,7 SelectTrigger,8 SelectValue,9} from "@/components/ui/pxl/select"10
11const northAmerica = [12 { label: "Eastern Standard Time", value: "est" },13 { label: "Central Standard Time", value: "cst" },14 { label: "Mountain Standard Time", value: "mst" },15 { label: "Pacific Standard Time", value: "pst" },16 { label: "Alaska Standard Time", value: "akst" },17 { label: "Hawaii Standard Time", value: "hst" },18]19
20const europeAfrica = [21 { label: "Greenwich Mean Time", value: "gmt" },22 { label: "Central European Time", value: "cet" },23 { label: "Eastern European Time", value: "eet" },24 { label: "Western European Summer Time", value: "west" },25 { label: "Central Africa Time", value: "cat" },26 { label: "East Africa Time", value: "eat" },27]28
29const asia = [30 { label: "Moscow Time", value: "msk" },31 { label: "India Standard Time", value: "ist" },32 { label: "China Standard Time", value: "cst_china" },33 { label: "Japan Standard Time", value: "jst" },34 { label: "Korea Standard Time", value: "kst" },35 { label: "Indonesia Central Standard Time", value: "ist_indonesia" },36]37
38const australiaPacific = [39 { label: "Australian Western Standard Time", value: "awst" },40 { label: "Australian Central Standard Time", value: "acst" },41 { label: "Australian Eastern Standard Time", value: "aest" },42 { label: "New Zealand Standard Time", value: "nzst" },43 { label: "Fiji Time", value: "fjt" },44]45
46const southAmerica = [47 { label: "Argentina Time", value: "art" },48 { label: "Bolivia Time", value: "bot" },49 { label: "Brasilia Time", value: "brt" },50 { label: "Chile Standard Time", value: "clt" },51]52
53const items = [54 { label: "Select a timezone", value: null },55 ...northAmerica,56 ...europeAfrica,57 ...asia,58 ...australiaPacific,59 ...southAmerica,60]61
62export default function SelectScrollableExample() {63 return (64 <Select items={items}>65 <SelectTrigger className="w-full max-w-64">66 <SelectValue />67 </SelectTrigger>68 <SelectContent>69 <SelectGroup>70 <SelectLabel>North America</SelectLabel>71 {northAmerica.map((item) => (72 <SelectItem key={item.value} value={item.value}>73 {item.label}74 </SelectItem>75 ))}76 </SelectGroup>77 <SelectGroup>78 <SelectLabel>Europe & Africa</SelectLabel>79 {europeAfrica.map((item) => (80 <SelectItem key={item.value} value={item.value}>81 {item.label}82 </SelectItem>83 ))}84 </SelectGroup>85 <SelectGroup>86 <SelectLabel>Asia</SelectLabel>87 {asia.map((item) => (88 <SelectItem key={item.value} value={item.value}>89 {item.label}90 </SelectItem>91 ))}92 </SelectGroup>93 <SelectGroup>94 <SelectLabel>Australia & Pacific</SelectLabel>95 {australiaPacific.map((item) => (96 <SelectItem key={item.value} value={item.value}>97 {item.label}98 </SelectItem>99 ))}100 </SelectGroup>101 <SelectGroup>102 <SelectLabel>South America</SelectLabel>103 {southAmerica.map((item) => (104 <SelectItem key={item.value} value={item.value}>105 {item.label}106 </SelectItem>107 ))}108 </SelectGroup>109 </SelectContent>110 </Select>111 )112}A select with icons.
1import { CountryFlag } from "@/components/ui/pxl/country-flag";2import {3 Select,4 SelectContent,5 SelectGroup,6 SelectItem,7 SelectLabel,8 SelectTrigger,9 SelectValue,10} from "@/components/ui/pxl/select";11
12const items = [13 { label: "Español", iso31661: "ES", value: "es" },14 { label: "Português", iso31661: "PT", value: "pt" },15 { label: "Français", iso31661: "FR", value: "fr" },16 { label: "Italiano", iso31661: "IT", value: "it" },17 { label: "Deutsch", iso31661: "DE", value: "de" },18 { label: "English", iso31661: "GB", value: "gb" },19 { label: "Русский", iso31661: "RU", value: "ru" },20 { label: "हिन्दी", iso31661: "IN", value: "hi" },21 { label: "বাংলা", iso31661: "BD", value: "bn" },22 { label: "العربية", iso31661: "SA", value: "ar" },23 { label: "日本語", iso31661: "JP", value: "jp" },24 { label: "한국어", iso31661: "KR", value: "kr" },25];26
27export default function SelectIconsExample() {28 return (29 <Select items={items} defaultValue="es">30 <SelectTrigger className="w-full max-w-48">31 <SelectValue className="text-start uppercase gap-2!">32 {(value) => {33 const match = items.find((item) => item.value === value);34
35 return match ? (36 <>37 <CountryFlag className="w-8! h-5!" code={match.iso31661} />38 {match.label}39 </>40 ) : value;41 }}42 </SelectValue>43 </SelectTrigger>44 <SelectContent>45 <SelectGroup>46 <SelectLabel>Language</SelectLabel>47 {items.map((item) => (48 <SelectItem className="uppercase" key={item.value} value={item.value}>49 <CountryFlag className="w-8! h-5!" code={item.iso31661} />50 {item.label}51 </SelectItem>52 ))}53 </SelectGroup>54 </SelectContent>55 </Select>56 );57}Invalid
Section titled “Invalid”Add the data-invalid attribute to the Field component and the aria-invalid attribute to the SelectTrigger component to show an error state.
1<Field data-invalid>2 <FieldLabel>Fruit</FieldLabel>3 <SelectTrigger aria-invalid>4 <SelectValue />5 </SelectTrigger>6</Field>API Reference
Section titled “API Reference”See the Base UI Select documentation.