UI Logo

Current UI

Select

Displays a list of options for the user to pick from—triggered by a button.

Radix UI
'use client'

import { useState } from 'react'
import {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectGroup,
  SelectLabel,
  SelectItem,
  SelectSeparator,
} from '../ui/Select'

export default function SelectDemo() {
  const [selectVal, setSelectVal] = useState('')

  return (
    <Select onValueChange={setSelectVal}>
      <SelectTrigger hasIcon className='w-[275px]' variant='default'>
        <SelectValue placeholder='Select a position...' />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          <SelectLabel>Design/UX</SelectLabel>
          <SelectItem value='Director, UX - Platform'>
            Director, UX - Platform
          </SelectItem>
        </SelectGroup>
        <SelectSeparator />
        <SelectGroup>
          <SelectLabel>Engineering</SelectLabel>
          <SelectItem value='Cloud SRE Senior Director'>
            Cloud SRE Senior Director
          </SelectItem>
          <SelectItem value='Senior Software Engineer'>
            Senior Software Engineer
          </SelectItem>
          <SelectItem value='Senior Manager - Security Architecture'>
            Senior Manager
          </SelectItem>
        </SelectGroup>
        <SelectSeparator />
        <SelectGroup>
          <SelectLabel>Marketing</SelectLabel>
          <SelectItem value='Director, PR & Communications'>
            Director, PR & Communications
          </SelectItem>
          <SelectItem value='Sr. Design Engineer'>
            Sr. Design Engineer
          </SelectItem>
        </SelectGroup>
      </SelectContent>
    </Select>
  )
}

Installation

Install the following dependencies:

npm install @radix-ui/react-select

Copy and paste the following code into your project.

'use client'

import * as React from 'react'
import * as SelectPrimitive from '@radix-ui/react-select'
import { cva, type VariantProps } from 'class-variance-authority'
import { ChevronDown, Check } from 'lucide-react'

import { cn } from '@/lib/utils'

const Select = SelectPrimitive.Root

const SelectValue = SelectPrimitive.Value

const selectTriggerVariants = cva(
  'flex w-full items-center justify-between gap-3 rounded-md border border-input px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
  {
    variants: {
      variant: {
        default: 'bg-background data-[placeholder]:text-muted-foreground',
        secondary:
          'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        ghost:
          'border-none shadow-none hover:bg-accent hover:text-accent-foreground',
      },
    },
    defaultVariants: {
      variant: 'default',
    },
  }
)

interface SelectTriggerProps
  extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>,
    VariantProps<typeof selectTriggerVariants> {
  hasIcon?: boolean | undefined
}

const SelectTrigger = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Trigger>,
  SelectTriggerProps
>(({ className, variant, hasIcon, children, ...props }, ref) => (
  <SelectPrimitive.Trigger
    ref={ref}
    className={cn(selectTriggerVariants({ variant }), className)}
    {...props}
  >
    {children}
    {hasIcon && (
      <SelectPrimitive.Icon asChild>
        <ChevronDown strokeWidth='1.5' className='h-5 w-5 opacity-75' />
      </SelectPrimitive.Icon>
    )}
  </SelectPrimitive.Trigger>
))

const SelectContent = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Portal>
    <SelectPrimitive.Content
      ref={ref}
      className={cn(
        'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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
      )}
      position='popper'
      {...props}
    >
      <SelectPrimitive.Viewport className='h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] p-1'>
        {children}
      </SelectPrimitive.Viewport>
    </SelectPrimitive.Content>
  </SelectPrimitive.Portal>
))

const SelectLabel = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Label
    ref={ref}
    className={cn('py-1.5 pl-8 pr-2 text-xs font-semibold', className)}
    {...props}
  >
    {children}
  </SelectPrimitive.Label>
))

const SelectItem = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> & {
    hasIndicator?: boolean | undefined
  }
>(({ className, children, hasIndicator, ...props }, ref) => {
  return (
    <SelectPrimitive.Item
      ref={ref}
      className={cn(
        'relative flex w-full cursor-pointer select-none items-center gap-5 rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[state=checked]:font-bold data-[disabled]:opacity-50',
        hasIndicator && 'data-[state=checked]:font-normal',
        className
      )}
      {...props}
    >
      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
      {hasIndicator && (
        <span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
          <SelectPrimitive.ItemIndicator>
            <Check className='h-4 w-4' />
          </SelectPrimitive.ItemIndicator>
        </span>
      )}
    </SelectPrimitive.Item>
  )
})

const SelectGroup = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Group>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Group ref={ref} className={cn('py-1', className)} {...props}>
    {children}
  </SelectPrimitive.Group>
))

const SelectSeparator = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Separator>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Separator
    ref={ref}
    className={cn('my-1 h-px bg-muted', className)}
    {...props}
  />
))

export {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectGroup,
  SelectLabel,
  SelectItem,
  SelectSeparator,
}

Update the import paths to match your project setup.

Anatomy

Import all parts and piece them together.

import {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectGroup,
  SelectLabel,
  SelectItem,
  SelectSeparator,
} from './ui/Select'
<Select>
  <SelectTrigger>
    <SelectValue />
    <SelectIcon />
  </SelectTrigger>

  <SelectPortal>
    <SelectContent>
      <SelectScrollUpButton />
      <SelectViewport>
        <SelectItem>
          <SelectItemText />
          <SelectItemIndicator />
        </SelectItem>

        <SelectGroup>
          <SelectLabel />
          <SelectItem>
            <SelectItemText />
            <SelectItemIndicator />
          </SelectItem>
        </SelectGroup>

        <SelectSeparator />
      </SelectViewport>
      <SelectScrollDownButton />
      <SelectArrow />
    </SelectContent>
  </SelectPortal>

</Select>