NimUI
ComponentsPrimitives

Select

Dropdown selection component built on Radix UI

The Select component is a composite dropdown selection control built on Radix UI Select. It provides accessible, keyboard-navigable dropdowns with support for grouped options, custom triggers, and CVA-based styling variants.

Import

import {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectGroup,
  SelectLabel,
  SelectItem,
} from '@nim-ui/components';

Variants

Default

Default Select
Code
<Select>
  <SelectTrigger className="w-[200px]">
    <SelectValue placeholder="Select a fruit" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="apple">Apple</SelectItem>
    <SelectItem value="banana">Banana</SelectItem>
    <SelectItem value="cherry">Cherry</SelectItem>
    <SelectItem value="grape">Grape</SelectItem>
    <SelectItem value="orange">Orange</SelectItem>
  </SelectContent>
</Select>

With Groups

Grouped Select
Code
<Select>
  <SelectTrigger className="w-[240px]">
    <SelectValue placeholder="Select a timezone" />
  </SelectTrigger>
  <SelectContent>
    <SelectGroup>
      <SelectLabel>North America</SelectLabel>
      <SelectItem value="est">Eastern (EST)</SelectItem>
      <SelectItem value="cst">Central (CST)</SelectItem>
      <SelectItem value="pst">Pacific (PST)</SelectItem>
    </SelectGroup>
    <SelectGroup>
      <SelectLabel>Europe</SelectLabel>
      <SelectItem value="gmt">Greenwich (GMT)</SelectItem>
      <SelectItem value="cet">Central European (CET)</SelectItem>
      <SelectItem value="eet">Eastern European (EET)</SelectItem>
    </SelectGroup>
  </SelectContent>
</Select>

Sizes

The SelectTrigger supports size variants through CVA styling.

Select Sizes
Code
{/* Small */}
<Select>
  <SelectTrigger className="w-[180px] h-8 text-sm">
    <SelectValue placeholder="Small" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="a">Option A</SelectItem>
    <SelectItem value="b">Option B</SelectItem>
  </SelectContent>
</Select>

{/* Medium (default) */}
<Select>
  <SelectTrigger className="w-[180px]">
    <SelectValue placeholder="Medium (default)" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="a">Option A</SelectItem>
    <SelectItem value="b">Option B</SelectItem>
  </SelectContent>
</Select>

{/* Large */}
<Select>
  <SelectTrigger className="w-[180px] h-12 text-lg">
    <SelectValue placeholder="Large" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="a">Option A</SelectItem>
    <SelectItem value="b">Option B</SelectItem>
  </SelectContent>
</Select>

States

Disabled

Disabled State
Code
<Select disabled>
  <SelectTrigger className="w-[200px]">
    <SelectValue placeholder="Disabled select" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="a">Option A</SelectItem>
  </SelectContent>
</Select>

Disabled Items

Disabled Individual Items
Code
<Select>
  <SelectTrigger className="w-[200px]">
    <SelectValue placeholder="Pick an option" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="available">Available</SelectItem>
    <SelectItem value="sold-out" disabled>Sold Out</SelectItem>
    <SelectItem value="coming-soon" disabled>Coming Soon</SelectItem>
    <SelectItem value="in-stock">In Stock</SelectItem>
  </SelectContent>
</Select>

Props

Select

NameTypeDefaultDescription
valuestring-Controlled value of the selected item
onValueChange(value: string) => void-Callback fired when the selected value changes
defaultValuestring-Initial selected value for uncontrolled usage
disabledbooleanfalseWhether the select is disabled

SelectTrigger

NameTypeDefaultDescription
classNamestring-Additional CSS classes for styling the trigger button
children*ReactNode-Trigger content, typically a SelectValue component

SelectValue

NameTypeDefaultDescription
placeholderstring-Text to display when no value is selected

SelectContent

NameTypeDefaultDescription
position'popper' | 'item-aligned''popper'Positioning mode for the dropdown content
classNamestring-Additional CSS classes for the dropdown container

SelectItem

NameTypeDefaultDescription
value*string-Unique value for this option
disabledbooleanfalseWhether this item is disabled
classNamestring-Additional CSS classes to apply
children*ReactNode-Display text for this option

SelectGroup

NameTypeDefaultDescription
classNamestring-Additional CSS classes for the group container

SelectLabel

NameTypeDefaultDescription
classNamestring-Additional CSS classes for the group label
children*ReactNode-Label text for the group

Usage Examples

Country Selector

function CountrySelector() {
  const [country, setCountry] = useState('');

  return (
    <div className="space-y-2">
      <label className="block text-sm font-medium">Country</label>
      <Select value={country} onValueChange={setCountry}>
        <SelectTrigger className="w-full">
          <SelectValue placeholder="Select your country" />
        </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            <SelectLabel>Americas</SelectLabel>
            <SelectItem value="us">United States</SelectItem>
            <SelectItem value="ca">Canada</SelectItem>
            <SelectItem value="mx">Mexico</SelectItem>
            <SelectItem value="br">Brazil</SelectItem>
          </SelectGroup>
          <SelectGroup>
            <SelectLabel>Europe</SelectLabel>
            <SelectItem value="uk">United Kingdom</SelectItem>
            <SelectItem value="de">Germany</SelectItem>
            <SelectItem value="fr">France</SelectItem>
            <SelectItem value="es">Spain</SelectItem>
          </SelectGroup>
          <SelectGroup>
            <SelectLabel>Asia</SelectLabel>
            <SelectItem value="jp">Japan</SelectItem>
            <SelectItem value="kr">South Korea</SelectItem>
            <SelectItem value="sg">Singapore</SelectItem>
          </SelectGroup>
        </SelectContent>
      </Select>
    </div>
  );
}

Form with Select

function ProfileForm() {
  const [role, setRole] = useState('');
  const [department, setDepartment] = useState('');

  return (
    <form className="space-y-4">
      <div>
        <label className="block text-sm font-medium mb-1">Role</label>
        <Select value={role} onValueChange={setRole}>
          <SelectTrigger className="w-full">
            <SelectValue placeholder="Select role" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="admin">Administrator</SelectItem>
            <SelectItem value="editor">Editor</SelectItem>
            <SelectItem value="viewer">Viewer</SelectItem>
          </SelectContent>
        </Select>
      </div>
      <div>
        <label className="block text-sm font-medium mb-1">Department</label>
        <Select value={department} onValueChange={setDepartment}>
          <SelectTrigger className="w-full">
            <SelectValue placeholder="Select department" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="engineering">Engineering</SelectItem>
            <SelectItem value="design">Design</SelectItem>
            <SelectItem value="marketing">Marketing</SelectItem>
            <SelectItem value="sales">Sales</SelectItem>
          </SelectContent>
        </Select>
      </div>
      <Button type="submit">Save Profile</Button>
    </form>
  );
}

Dynamic Options

function DynamicSelect({ options, loading }) {
  return (
    <Select disabled={loading}>
      <SelectTrigger className="w-[240px]">
        <SelectValue placeholder={loading ? 'Loading...' : 'Select an option'} />
      </SelectTrigger>
      <SelectContent>
        {options.map((option) => (
          <SelectItem key={option.value} value={option.value}>
            {option.label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

Accessibility

The Select component is built on Radix UI and follows WAI-ARIA best practices:

  • Uses role="combobox" on the trigger with aria-expanded state
  • Dropdown uses role="listbox" with role="option" on each item
  • Supports aria-label and aria-labelledby for screen reader announcements
  • Full keyboard navigation with type-ahead support
  • Focus is managed automatically when opening and closing
  • Disabled state properly communicated to assistive technology

Keyboard Support

KeyAction
Space / EnterOpens the select dropdown or selects focused item
Arrow DownOpens dropdown or moves focus to next item
Arrow UpMoves focus to previous item
HomeMoves focus to first item
EndMoves focus to last item
EscapeCloses the dropdown
Type-aheadFocuses matching item by typing characters

Best Practices

  • Always provide a descriptive placeholder via SelectValue
  • Pair Select with a visible label for form context
  • Use SelectGroup and SelectLabel for long lists of options
  • Disable individual items rather than hiding them to maintain context
  • Provide clear, concise option labels
  • Radio - Radio group for visible single selection
  • Input - Text input for free-form entry
  • Checkbox - Multiple selection control
  • Button - Interactive button for actions

On this page