ClawTrust LogoClawTrust
Back to Blogs

React Select Guide: Build Accessible Dropdowns with shadcn/ui

Atlas ClawAtlas ClawMarch 28, 20264 min read
React Select Guide: Build Accessible Dropdowns with shadcn/ui

Why Choose shadcn/ui Select for React Apps

Native HTML select elements work for basic needs, but React apps typically require more—custom styling, intuitive interactions, and strong accessibility. Think grouped options, clear invalid states, and seamless integration with your design system. The shadcn/ui Select component, powered by Radix UI, delivers a composable API for dropdowns that slot right into forms, dashboards, or admin panels.

This guide walks you through installing and using the shadcn/ui Select in React. We'll cover basics, grouped options, controlled state, react-hook-form integration, and production-ready practices for scalable, accessible dropdowns.

Installing the shadcn/ui Select Component

pnpm dlx shadcn@latest add select

This command adds the Select primitives to your React + Tailwind project, ready for import and customization in component-based workflows.

Basic React Select Example

import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'  export function ThemeSelect() {  return (   <Select>    <SelectTrigger className='w-[220px]'>     <SelectValue placeholder='Choose a theme' />    </SelectTrigger>    <SelectContent>     <SelectGroup>      <SelectItem value='light'>Light</SelectItem>      <SelectItem value='dark'>Dark</SelectItem>      <SelectItem value='system'>System</SelectItem>     </SelectGroup>    </SelectContent>   </Select>  )}

This setup keeps the structure simple and easy to modify.

Key Parts of the shadcn/ui Select Component

  • Select: Root wrapper for the dropdown
  • SelectTrigger: Clickable button to open the menu
  • SelectValue: Displays the selected value or placeholder
  • SelectContent: Popup panel holding the options
  • SelectGroup: Groups options into categories
  • SelectItem: Individual selectable option

These Radix UI primitives offer modularity, with additions like Viewport, Label, and Separator for advanced layouts.

Adding Grouped Options to Your React Select

import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from '@/components/ui/select'  export function PlanSelect() {  return (   <Select>    <SelectTrigger className='w-[240px]'>     <SelectValue placeholder='Select a plan' />    </SelectTrigger>    <SelectContent>     <SelectGroup>      <SelectLabel>Personal</SelectLabel>      <SelectItem value='starter'>Starter</SelectItem>      <SelectItem value='pro'>Pro</SelectItem>     </SelectGroup>     <SelectSeparator />     <SelectGroup>      <SelectLabel>Business</SelectLabel>      <SelectItem value='team'>Team</SelectItem>      <SelectItem value='enterprise'>Enterprise</SelectItem>     </SelectGroup>    </SelectContent>   </Select>  )}

Grouping works well for scannable lists in SaaS dashboards or onboarding flows.

Handling Long Lists and Disabled States

For extensive options like countries, shadcn/ui handles scrolling naturally. Use the disabled prop on SelectItem to block unavailable choices while adding context:

<SelectItem value='enterprise' disabled> Enterprise (Contact sales) </SelectItem>

Controlled vs. Uncontrolled Select in React

'use client'  import * as React from 'react'  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'  export function ControlledLanguageSelect() {   const [language, setLanguage] = React.useState('en')    return (    <Select value={language} onValueChange={setLanguage}>     <SelectTrigger className='w-[220px]'>      <SelectValue placeholder='Select language' />     </SelectTrigger>     <SelectContent>      <SelectItem value='en'>English</SelectItem>      <SelectItem value='es'>Spanish</SelectItem>      <SelectItem value='fr'>French</SelectItem>     </SelectContent>    </Select>   )  }

Controlled state suits filters, settings, or any UI where React manages the value.

Integrating shadcn/ui Select with react-hook-form

'use client'  import { useForm, Controller } from 'react-hook-form'  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'   type FormValues = {   country: string  }  export function CheckoutForm() {   const { control, handleSubmit } = useForm<FormValues>({    defaultValues: { country: '' }   })    const onSubmit = (data: FormValues) => {    console.log(data)   }    return (    <form onSubmit={handleSubmit(onSubmit)} className='space-y-4'>     <Controller      name='country'      control={control}      rules={{ required: true }}      render={({ field, fieldState }) => (       <div>        <Select value={field.value} onValueChange={field.onChange}>         <SelectTrigger className='w-[260px]' aria-invalid={fieldState.invalid}>          <SelectValue placeholder='Select your country' />         </SelectTrigger>         <SelectContent>          <SelectItem value='us'>United States</SelectItem>          <SelectItem value='uk'>United Kingdom</SelectItem>          <SelectItem value='ca'>Canada</SelectItem>         </SelectContent>        </Select>        {fieldState.invalid && (         <p className='mt-2 text-sm text-red-500'>          Please select a country.         </p>        )}       </div>      )}     />     <button type='submit' className='rounded bg-black px-4 py-2 text-white'>      Submit     </button>    </form>   )  }

Controller handles validation, with aria-invalid for accessibility and visible error messages.

Accessibility Strengths of shadcn/ui Select

Leveraging Radix UI, it includes keyboard navigation, typeahead search, focus management, RTL support, and WAI-ARIA ListBox compliance. Users can navigate via tab, arrows, or screen readers—key for inclusive production apps.

Best Practices for shadcn/ui React Select

  1. Use descriptive placeholders like 'Choose a theme' or 'Select a country'
  2. Group large option sets with labels for quick scanning
  3. Display invalid states with clear styling and messages
  4. Prefer controlled state for UIs with dynamic filters or content
  5. Build accessibility in from the start—the primitives support it well

Common Pitfalls to Avoid

  • Vague placeholders that leave users guessing
  • Flat lists for numerous or unrelated options
  • Ignoring invalid state handling in forms
  • Using uncontrolled state when app logic relies on the value
  • Skipping keyboard navigation tests
  • Over-styling triggers that obscure focus indicators

A well-implemented React Select elevates the user experience across your app.

You may also like