Select
selectuiDropdown select primitive — single-select with optional groups, per-item descriptions, Radix UI popover mode, and zero-JS styled NativeSelect for lightweight/mobile use.
When to use which
Decision GuideSelect is the headless primitive for flat options. Advance Select is the searchable combobox. Cascade and Tree selectors are designed specifically for hierarchical and nested datasets.
| Component | Role | Use when |
|---|---|---|
| Selectcurrent Short, flat option lists (< 20 items) where typing to filter is unnecessary. | Primitive Dropdown | |
| AdvanceSelect Large datasets requiring fuzzy search, multi-select tag pills, or remote option filtering. | Searchable Combobox | |
| CascadeSelect Sequential multi-level drilldowns (e.g. Country > State > City, Category > Subcategory). | Hierarchical Picker | |
| TreeSelect Deeply nested taxonomy structures, folder trees, or parent-child permission nodes. | Tree Picker |
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/select.json$npx shadcn@latest add https://uipkge.dev/r/react/select.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/select.json$bunx shadcn@latest add https://uipkge.dev/r/react/select.jsonnpx shadcn@latest add @uipkge-react/selectInstalls to:components/ui/select/Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
size | 'sm' | 'default' | 'lg' | default | optional |
state | 'default' | 'error' | 'success' | default | optional |
loading | boolean | false | optional |
Schema
Type aliases from this item's source — use them to shape the data you pass in.
NativeSelectOptioninterface NativeSelectOption {
label: string
value: string | number
disabled?: boolean
}npm dependencies
Used by
Files installed (3)
components/ui/select/select.tsx8.1 kB
'use client' import * as React from 'react' import * as SelectPrimitive from '@radix-ui/react-select' import { Check, ChevronDown, ChevronUp, Loader } from 'lucide-react' import { cn } from '@/lib/utils' const Select = SelectPrimitive.Root const SelectGroup = SelectPrimitive.Group const SelectValue = React.forwardRef< React.ElementRef<typeof SelectPrimitive.Value>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Value> >((props, ref) => <SelectPrimitive.Value ref={ref} data-slot="select-value" {...props} />) SelectValue.displayName = SelectPrimitive.Value.displayName const triggerSizeClasses = { sm: 'h-8 text-sm px-2.5 py-1.5', default: 'h-9 text-sm px-3 py-2', lg: 'h-11 text-base px-4 py-2.5', } const triggerStateClasses = { default: 'border-input dark:hover:bg-input/50', error: 'border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive', success: 'border-success focus-visible:border-success', } export interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> { size?: 'sm' | 'default' | 'lg' state?: 'default' | 'error' | 'success' loading?: boolean } const SelectTrigger = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Trigger>, SelectTriggerProps>( ({ className, children, size = 'default', state = 'default', loading = false, disabled, ...props }, ref) => ( <SelectPrimitive.Trigger ref={ref} data-uipkge="" data-slot="select-trigger" data-size={size} data-state-value={state} disabled={disabled || loading} aria-busy={loading} className={cn( "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex w-full items-center justify-between gap-2 rounded-md border bg-transparent text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", triggerSizeClasses[size], triggerStateClasses[state], className, )} {...props} > {children} {loading ? ( <Loader className="size-4 animate-spin opacity-50" aria-hidden="true" /> ) : ( <SelectPrimitive.Icon asChild> <ChevronDown className="size-4 opacity-50" aria-hidden="true" /> </SelectPrimitive.Icon> )} </SelectPrimitive.Trigger> ), ) SelectTrigger.displayName = SelectPrimitive.Trigger.displayName const SelectScrollUpButton = React.forwardRef< React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton> >(({ className, children, ...props }, ref) => ( <SelectPrimitive.ScrollUpButton ref={ref} data-uipkge="" data-slot="select-scroll-up-button" className={cn('flex cursor-default items-center justify-center py-1', className)} {...props} > {children ?? <ChevronUp className="size-4" aria-hidden="true" />} </SelectPrimitive.ScrollUpButton> )) SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName const SelectScrollDownButton = React.forwardRef< React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton> >(({ className, children, ...props }, ref) => ( <SelectPrimitive.ScrollDownButton ref={ref} data-uipkge="" data-slot="select-scroll-down-button" className={cn('flex cursor-default items-center justify-center py-1', className)} {...props} > {children ?? <ChevronDown className="size-4" aria-hidden="true" />} </SelectPrimitive.ScrollDownButton> )) SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName const SelectContent = React.forwardRef< React.ElementRef<typeof SelectPrimitive.Content>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> >(({ className, children, position = 'popper', ...props }, ref) => ( <SelectPrimitive.Portal> <SelectPrimitive.Content ref={ref} data-uipkge="" data-slot="select-content" position={position} className={cn( 'bg-popover text-popover-foreground motion-safe:data-[state=open]:animate-in motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=open]:ease-emphasized motion-safe:data-[state=open]:blur-in-2 motion-safe:data-[state=closed]:blur-out-2 motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=open]:fade-in-0 motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:zoom-in-95 motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=left]:slide-in-from-right-2 motion-safe:data-[side=right]:slide-in-from-left-2 motion-safe:data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md motion-safe:data-[state=closed]:duration-[var(--dur-exit)] motion-safe:data-[state=open]:duration-200', position === 'popper' && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className, )} {...props} > <SelectScrollUpButton /> <SelectPrimitive.Viewport className={cn( 'p-1', position === 'popper' && 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1', )} > {children} </SelectPrimitive.Viewport> <SelectScrollDownButton /> </SelectPrimitive.Content> </SelectPrimitive.Portal> )) SelectContent.displayName = SelectPrimitive.Content.displayName const SelectLabel = React.forwardRef< React.ElementRef<typeof SelectPrimitive.Label>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> >(({ className, ...props }, ref) => ( <SelectPrimitive.Label ref={ref} data-uipkge="" data-slot="select-label" className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)} {...props} /> )) SelectLabel.displayName = SelectPrimitive.Label.displayName const SelectItem = React.forwardRef< React.ElementRef<typeof SelectPrimitive.Item>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> >(({ className, children, ...props }, ref) => ( <SelectPrimitive.Item ref={ref} data-uipkge="" data-slot="select-item" className={cn( "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none 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", className, )} {...props} > <span className="absolute right-2 flex size-3.5 items-center justify-center"> <SelectPrimitive.ItemIndicator> <Check className="size-4" aria-hidden="true" /> </SelectPrimitive.ItemIndicator> </span> <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> </SelectPrimitive.Item> )) SelectItem.displayName = SelectPrimitive.Item.displayName const SelectItemText = SelectPrimitive.ItemText const SelectSeparator = React.forwardRef< React.ElementRef<typeof SelectPrimitive.Separator>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> >(({ className, ...props }, ref) => ( <SelectPrimitive.Separator ref={ref} data-uipkge="" data-slot="select-separator" className={cn('bg-border pointer-events-none -mx-1 my-1 h-px', className)} {...props} /> )) SelectSeparator.displayName = SelectPrimitive.Separator.displayName export { Select, SelectContent, SelectGroup, SelectItem, SelectItemText, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, }components/ui/select/NativeSelect.tsx2.5 kB
'use client' import * as React from 'react' import { ChevronDown } from 'lucide-react' import { cn } from '@/lib/utils' export interface NativeSelectOption { label: string value: string | number disabled?: boolean } export interface NativeSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { options?: (NativeSelectOption | string)[] sizeVariant?: 'sm' | 'md' | 'lg' selectClassName?: string } const sizeClasses: Record<string, string> = { sm: 'h-8 text-xs pl-2.5 pr-8', md: 'h-9 text-sm pl-3 pr-9', lg: 'h-11 text-base pl-4 pr-10', } const iconSizes: Record<string, string> = { sm: 'size-3.5 right-2.5', md: 'size-4 right-3', lg: 'size-5 right-3.5', } export const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>( ({ className, selectClassName, options, sizeVariant = 'md', disabled, children, ...props }, ref) => { return ( <div data-uipkge="" data-slot="native-select-wrapper" className={cn('relative inline-flex w-full items-center', className)} > <select ref={ref} data-slot="native-select" disabled={disabled} className={cn( 'border-input bg-background w-full appearance-none rounded-md border shadow-xs transition-[color,box-shadow]', 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none', 'disabled:bg-muted/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50', sizeClasses[sizeVariant], selectClassName, )} {...props} > {options && options.length > 0 ? options.map((opt) => { const isString = typeof opt === 'string' const value = isString ? opt : opt.value const label = isString ? opt : opt.label const isDisabled = !isString && opt.disabled return ( <option key={String(value)} value={value} disabled={isDisabled}> {label} </option> ) }) : children} </select> <ChevronDown data-slot="native-select-icon" aria-hidden="true" className={cn( 'text-muted-foreground pointer-events-none absolute transition-opacity', disabled && 'opacity-50', iconSizes[sizeVariant], )} /> </div> ) }, ) NativeSelect.displayName = 'NativeSelect'components/ui/select/index.ts0.3 kB
export { Select, SelectContent, SelectGroup, SelectItem, SelectItemText, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from './select' export { NativeSelect, type NativeSelectProps, type NativeSelectOption } from './NativeSelect'
Raw manifest:https://uipkge.dev/r/react/select.json