vera logoVera UI
ComponentsForm Components

Select

A select component built on Radix UI's Select primitive that allows users to choose a single value from a dropdown list of options. Features keyboard navigation, accessibility, and customizable styling.

Installation

pnpm add @helgadigitals/vera-ui
npm install @helgadigitals/vera-ui
yarn add @helgadigitals/vera-ui

Usage

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@helgadigitals/vera-ui"export default function Example() {return (  <Select>    <SelectTrigger className="w-[180px]">      <SelectValue placeholder="Select a fruit" />    </SelectTrigger>    <SelectContent>      <SelectItem value="apple">Apple</SelectItem>      <SelectItem value="banana">Banana</SelectItem>      <SelectItem value="orange">Orange</SelectItem>    </SelectContent>  </Select>)}

Examples

With Groups

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,SelectGroup,SelectLabel } from "@helgadigitals/vera-ui"export default function GroupedExample() {return (  <Select>    <SelectTrigger className="w-[180px]">      <SelectValue placeholder="Select a food" />    </SelectTrigger>    <SelectContent>      <SelectGroup>        <SelectLabel>Fruits</SelectLabel>        <SelectItem value="apple">Apple</SelectItem>        <SelectItem value="banana">Banana</SelectItem>        <SelectItem value="orange">Orange</SelectItem>      </SelectGroup>      <SelectGroup>        <SelectLabel>Vegetables</SelectLabel>        <SelectItem value="carrot">Carrot</SelectItem>        <SelectItem value="broccoli">Broccoli</SelectItem>        <SelectItem value="spinach">Spinach</SelectItem>      </SelectGroup>    </SelectContent>  </Select>)}

Controlled Select

import { useState } from "react"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@helgadigitals/vera-ui"export default function ControlledExample() {const [selectedValue, setSelectedValue] = useState("")return (  <div className="space-y-4">    <div className="space-y-2">      <label className="text-sm font-medium">Choose a country</label>      <Select value={selectedValue} onValueChange={setSelectedValue}>        <SelectTrigger className="w-[200px]">          <SelectValue placeholder="Select country" />        </SelectTrigger>        <SelectContent>          <SelectItem value="us">United States</SelectItem>          <SelectItem value="ca">Canada</SelectItem>          <SelectItem value="uk">United Kingdom</SelectItem>          <SelectItem value="au">Australia</SelectItem>        </SelectContent>      </Select>    </div>        {selectedValue && (      <p className="text-sm text-muted-foreground">        Selected: {selectedValue}      </p>    )}  </div>)}

With Groups and Labels

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,SelectGroup,SelectLabel,SelectSeparator} from "@helgadigitals/vera-ui"export default function GroupedExample() {return (  <div className="space-y-2">    <label className="text-sm font-medium">Choose a programming language</label>    <Select>      <SelectTrigger className="w-[250px]">        <SelectValue placeholder="Select language" />      </SelectTrigger>      <SelectContent>        <SelectGroup>          <SelectLabel>Frontend</SelectLabel>          <SelectItem value="javascript">JavaScript</SelectItem>          <SelectItem value="typescript">TypeScript</SelectItem>          <SelectItem value="css">CSS</SelectItem>        </SelectGroup>                <SelectSeparator />                <SelectGroup>          <SelectLabel>Backend</SelectLabel>          <SelectItem value="python">Python</SelectItem>          <SelectItem value="java">Java</SelectItem>          <SelectItem value="go">Go</SelectItem>          <SelectItem value="rust">Rust</SelectItem>        </SelectGroup>                <SelectSeparator />                <SelectGroup>          <SelectLabel>Mobile</SelectLabel>          <SelectItem value="swift">Swift</SelectItem>          <SelectItem value="kotlin">Kotlin</SelectItem>          <SelectItem value="dart">Dart</SelectItem>        </SelectGroup>      </SelectContent>    </Select>  </div>)}

Different Sizes

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@helgadigitals/vera-ui"export default function SizesExample() {return (  <div className="space-y-4">    <div className="space-y-2">      <label className="text-sm font-medium">Small Select</label>      <Select>        <SelectTrigger size="sm" className="w-[150px]">          <SelectValue placeholder="Small" />        </SelectTrigger>        <SelectContent>          <SelectItem value="option1">Option 1</SelectItem>          <SelectItem value="option2">Option 2</SelectItem>        </SelectContent>      </Select>    </div>    <div className="space-y-2">      <label className="text-sm font-medium">Default Select</label>      <Select>        <SelectTrigger className="w-[180px]">          <SelectValue placeholder="Default" />        </SelectTrigger>        <SelectContent>          <SelectItem value="option1">Option 1</SelectItem>          <SelectItem value="option2">Option 2</SelectItem>        </SelectContent>      </Select>    </div>  </div>)}

Disabled States

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@helgadigitals/vera-ui"export default function DisabledExample() {return (  <div className="space-y-6">    {/* Disabled select */}    <div className="space-y-2">      <label className="text-sm font-medium">Disabled Select</label>      <Select disabled>        <SelectTrigger className="w-[200px]">          <SelectValue placeholder="Disabled select" />        </SelectTrigger>        <SelectContent>          <SelectItem value="option1">Option 1</SelectItem>          <SelectItem value="option2">Option 2</SelectItem>        </SelectContent>      </Select>    </div>    {/* Individual disabled items */}    <div className="space-y-2">      <label className="text-sm font-medium">With Disabled Items</label>      <Select>        <SelectTrigger className="w-[200px]">          <SelectValue placeholder="Select option" />        </SelectTrigger>        <SelectContent>          <SelectItem value="available1">Available Option 1</SelectItem>          <SelectItem value="disabled" disabled>            Disabled Option          </SelectItem>          <SelectItem value="available2">Available Option 2</SelectItem>        </SelectContent>      </Select>    </div>  </div>)}

With Icons

import { User, Mail, Phone, MessageSquare } from "lucide-react"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@helgadigitals/vera-ui"export default function WithIconsExample() {return (  <div className="space-y-2">    <label className="text-sm font-medium">Contact method</label>    <Select>      <SelectTrigger className="w-[220px]">        <SelectValue placeholder="Choose contact method" />      </SelectTrigger>      <SelectContent>        <SelectItem value="email">          <Mail className="mr-2 h-4 w-4" />          Email        </SelectItem>        <SelectItem value="phone">          <Phone className="mr-2 h-4 w-4" />          Phone        </SelectItem>        <SelectItem value="message">          <MessageSquare className="mr-2 h-4 w-4" />          Message        </SelectItem>        <SelectItem value="in-person">          <User className="mr-2 h-4 w-4" />          In Person        </SelectItem>      </SelectContent>    </Select>  </div>)}

Form Integration

Current Values:

{
  "country": "none",
  "theme": "system"
}
import { useForm, Controller } from "react-hook-form"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,Button } from "@helgadigitals/vera-ui"export default function FormExample() {const { control, handleSubmit, formState: { errors } } = useForm()const onSubmit = (data) => {  console.log(data)}return (  <form onSubmit={handleSubmit(onSubmit)} className="space-y-6">    <div className="space-y-2">      <label className="text-sm font-medium">Country *</label>      <Controller        name="country"        control={control}        rules={{ required: "Please select a country" }}        render={({ field }) => (          <Select onValueChange={field.onChange} value={field.value || ""}>            <SelectTrigger className="w-full">              <SelectValue placeholder="Select your country" />            </SelectTrigger>            <SelectContent>              <SelectItem value="us">United States</SelectItem>              <SelectItem value="ca">Canada</SelectItem>              <SelectItem value="uk">United Kingdom</SelectItem>              <SelectItem value="au">Australia</SelectItem>              <SelectItem value="de">Germany</SelectItem>              <SelectItem value="fr">France</SelectItem>            </SelectContent>          </Select>        )}      />      {errors.country && (        <p className="text-sm text-destructive">          {errors.country.message}        </p>      )}    </div>    <div className="space-y-2">      <label className="text-sm font-medium">Theme preference</label>      <Controller        name="theme"        control={control}        defaultValue="system"        render={({ field }) => (          <Select onValueChange={field.onChange} value={field.value}>            <SelectTrigger className="w-full">              <SelectValue />            </SelectTrigger>            <SelectContent>              <SelectItem value="light">Light</SelectItem>              <SelectItem value="dark">Dark</SelectItem>              <SelectItem value="system">System</SelectItem>            </SelectContent>          </Select>        )}      />    </div>    <Button type="submit">Submit</Button>  </form>)}

Type to search through 193 countries

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@helgadigitals/vera-ui"const countries = [{ value: "af", label: "Afghanistan" },{ value: "al", label: "Albania" },{ value: "dz", label: "Algeria" },{ value: "ad", label: "Andorra" },{ value: "ao", label: "Angola" },// ... many more countries{ value: "us", label: "United States" },{ value: "uy", label: "Uruguay" },{ value: "uz", label: "Uzbekistan" },{ value: "vu", label: "Vanuatu" },{ value: "ve", label: "Venezuela" },{ value: "vn", label: "Vietnam" },{ value: "ye", label: "Yemen" },{ value: "zm", label: "Zambia" },{ value: "zw", label: "Zimbabwe" },]export default function LongListExample() {return (  <div className="space-y-2">    <label className="text-sm font-medium">Country</label>    <Select>      <SelectTrigger className="w-[250px]">        <SelectValue placeholder="Select a country" />      </SelectTrigger>      <SelectContent>        {countries.map((country) => (          <SelectItem key={country.value} value={country.value}>            {country.label}          </SelectItem>        ))}      </SelectContent>    </Select>  </div>)}

API Reference

Select

Root component that provides context for all other select components.

Prop

Type

SelectTrigger

The button that triggers the select dropdown.

Prop

Type

SelectContent

The dropdown content that contains the select options.

Prop

Type

SelectValue

Displays the selected value or placeholder.

Prop

Type

SelectItem

An option in the select dropdown.

Prop

Type

SelectGroup

Groups related select items together.

Prop

Type

SelectLabel

A label for a group of select items.

Prop

Type

SelectSeparator

A visual separator between select items or groups.

PropTypeDefaultDescription
classNamestring-Additional CSS classes

Accessibility

The Select component follows WAI-ARIA guidelines:

  • Keyboard Navigation:
    • Space or Enter opens the dropdown
    • Arrow keys navigate between options
    • Type to search for options
    • Escape closes the dropdown
  • Screen Readers: Proper ARIA attributes and announcements
  • Focus Management: Focus is properly managed between trigger and content
  • Selection: Selected value is properly announced to screen readers

Search Functionality: Users can type while the dropdown is open to quickly find options. The component will automatically highlight matching items.

Data Attributes

The following data attributes are automatically applied and can be used for styling:

SelectTrigger

AttributeValuesDescription
data-state"open" | "closed"The open state of the select
data-disabledPresent when disabledApplied when the select is disabled
data-placeholderPresent when placeholder is shownApplied when no value is selected

SelectItem

AttributeValuesDescription
data-state"checked" | "unchecked"Whether the item is selected
data-highlightedPresent when highlightedApplied when the item is highlighted
data-disabledPresent when disabledApplied when the item is disabled

Best Practices

Provide Clear Labels

Always include descriptive labels for your select components:

// Good: Clear label
<div className="space-y-2">
  <label htmlFor="country-select" className="text-sm font-medium">
    Country
  </label>
  <Select>
    <SelectTrigger id="country-select">
      <SelectValue placeholder="Select your country" />
    </SelectTrigger>
    {/* ... */}
  </Select>
</div>

Use Appropriate Placeholder Text

Placeholder text should guide users about what to select:

// Good: Descriptive placeholder
<SelectValue placeholder="Choose your preferred language" />

// Avoid: Generic placeholder
<SelectValue placeholder="Select..." />

Use groups and labels to organize related options:

<SelectContent>
  <SelectGroup>
    <SelectLabel>Popular Frameworks</SelectLabel>
    <SelectItem value="react">React</SelectItem>
    <SelectItem value="vue">Vue</SelectItem>
  </SelectGroup>
  <SelectSeparator />
  <SelectGroup>
    <SelectLabel>Other Options</SelectLabel>
    <SelectItem value="angular">Angular</SelectItem>
    <SelectItem value="svelte">Svelte</SelectItem>
  </SelectGroup>
</SelectContent>

Styling

The Select component supports custom styling through CSS classes:

<Select>
  <SelectTrigger className="w-full border-blue-300 focus:border-blue-500">
    <SelectValue />
  </SelectTrigger>
  <SelectContent className="bg-blue-50">
    <SelectItem 
      value="option1" 
      className="hover:bg-blue-100 focus:bg-blue-100"
    >
      Option 1
    </SelectItem>
  </SelectContent>
</Select>