UIPackage

Select

React control
Edit on GitHub

Dropdown select primitive — single-select, with optional groups and per-item descriptions. Built on Radix UI with full keyboard navigation.

Also available for Vue ->

Installation

$ npx shadcn@latest add https://react.uipkge.dev/r/react/select.json

Or with the named registry: npx shadcn@latest add @uipkge-react/select

Examples

Props

Name Type / Values Default Required
size 'sm' | 'default' | 'lg' default optional
state 'default' | 'error' | 'success' default optional
loading boolean false optional

Dependencies

Used by

Files (2)

  • components/ui/select/select.tsx 8.3 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 = React.forwardRef<
      React.ElementRef<typeof SelectPrimitive.Root>,
      React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>
    >((props, ref) => <SelectPrimitive.Root {...props} ref={ref} data-uipkge="" data-slot="select" />)
    Select.displayName = SelectPrimitive.Root.displayName
    
    const SelectGroup = React.forwardRef<
      React.ElementRef<typeof SelectPrimitive.Group>,
      React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>
    >((props, ref) => <SelectPrimitive.Group {...props} ref={ref} data-uipkge="" data-slot="select-group" />)
    SelectGroup.displayName = SelectPrimitive.Group.displayName
    
    const SelectValue = React.forwardRef<
      React.ElementRef<typeof SelectPrimitive.Value>,
      React.ComponentPropsWithoutRef<typeof SelectPrimitive.Value>
    >((props, ref) => <SelectPrimitive.Value {...props} ref={ref} data-uipkge="" data-slot="select-value" />)
    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-[var(--success)] focus-visible:border-[var(--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" />
        ) : (
          <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=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',
            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/index.ts 0.2 kB
    export {
      Select,
      SelectContent,
      SelectGroup,
      SelectItem,
      SelectItemText,
      SelectLabel,
      SelectScrollDownButton,
      SelectScrollUpButton,
      SelectSeparator,
      SelectTrigger,
      SelectValue,
    } from './select'

Raw manifest: https://react.uipkge.dev/r/react/select.json