RadCN
Inputsreadycomponentready

Select

A custom select primitive with trigger, value, grouped options, selected indicators, scroll affordances, and native hidden form values.

Importimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'radcn/select'
Preview

Live package example

Render the upstream fruit Select and grouped timezone Select examples with RadCN package primitives and app-owned option data.

Demo and Scrollable

Render the upstream fruit Select and grouped timezone Select examples with RadCN package primitives and app-owned option data.

Preview
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectScrollDownButton,
  SelectScrollUpButton,
  SelectTrigger,
  SelectValue,
  SelectViewport,
} from 'radcn/select'

const fruits = [
  ['apple', 'Apple'],
  ['banana', 'Banana'],
  ['blueberry', 'Blueberry'],
  ['grapes', 'Grapes'],
  ['pineapple', 'Pineapple'],
]

const timezones = [
  ['North America', [
    ['est', 'Eastern Standard Time (EST)'],
    ['cst', 'Central Standard Time (CST)'],
    ['mst', 'Mountain Standard Time (MST)'],
    ['pst', 'Pacific Standard Time (PST)'],
    ['akst', 'Alaska Standard Time (AKST)'],
    ['hst', 'Hawaii Standard Time (HST)'],
  ]],
  ['Europe & Africa', [
    ['gmt', 'Greenwich Mean Time (GMT)'],
    ['cet', 'Central European Time (CET)'],
    ['eet', 'Eastern European Time (EET)'],
    ['west', 'Western European Summer Time (WEST)'],
    ['cat', 'Central Africa Time (CAT)'],
    ['eat', 'East Africa Time (EAT)'],
  ]],
  ['Asia', [
    ['msk', 'Moscow Time (MSK)'],
    ['ist', 'India Standard Time (IST)'],
    ['cst_china', 'China Standard Time (CST)'],
    ['jst', 'Japan Standard Time (JST)'],
    ['kst', 'Korea Standard Time (KST)'],
    ['ist_indonesia', 'Indonesia Central Standard Time (WITA)'],
  ]],
  ['Australia & Pacific', [
    ['awst', 'Australian Western Standard Time (AWST)'],
    ['acst', 'Australian Central Standard Time (ACST)'],
    ['aest', 'Australian Eastern Standard Time (AEST)'],
    ['nzst', 'New Zealand Standard Time (NZST)'],
    ['fjt', 'Fiji Time (FJT)'],
  ]],
  ['South America', [
    ['art', 'Argentina Time (ART)'],
    ['bot', 'Bolivia Time (BOT)'],
    ['brt', 'Brasilia Time (BRT)'],
    ['clt', 'Chile Standard Time (CLT)'],
  ]],
]

export function SelectPreview() {
  return (
    <>
      <Select name="fruit" style="width:180px;">
        <SelectTrigger ariaLabel="Fruit">
          <SelectValue placeholder="Select a fruit" />
        </SelectTrigger>
        <SelectContent>
          <SelectViewport>
            <SelectGroup>
              <SelectLabel>Fruits</SelectLabel>
              {fruits.map(([value, label]) => (
                <SelectItem textValue={label} value={value}>{label}</SelectItem>
              ))}
            </SelectGroup>
          </SelectViewport>
        </SelectContent>
      </Select>

      <Select name="timezone" style="width:280px;">
        <SelectTrigger ariaLabel="Timezone">
          <SelectValue placeholder="Select a timezone" />
        </SelectTrigger>
        <SelectContent>
          <SelectScrollUpButton />
          <SelectViewport style="max-height:12rem;overflow:auto;">
            {timezones.map(([group, options]) => (
              <SelectGroup>
                <SelectLabel>{group}</SelectLabel>
                {options.map(([value, label]) => (
                  <SelectItem textValue={label} value={value}>{label}</SelectItem>
                ))}
              </SelectGroup>
            ))}
          </SelectViewport>
          <SelectScrollDownButton />
        </SelectContent>
      </Select>
    </>
  )
}

Installation

Intended future install command. RadCN is private and not published to npm yet, so this snippet documents the target user-facing API rather than something external consumers can run today.

pnpm add radcn # intended future package
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'radcn/select'

Theming

RadCN tokens read the resolved theme from the document. Store the user's preference separately, then resolve system preferences to a concrete light or dark theme before setting package tokens.

<html data-radcn-theme-mode="system" data-radcn-theme="dark">
  ...
</html>

Accessibility

  • SelectTrigger renders a button with combobox semantics after enhancement while SelectViewport owns listbox semantics.
  • SelectItem renders role="option" with aria-selected and selected indicator hooks.
  • SelectGroup and SelectLabel provide visible grouping for fruit and timezone option sets.
  • When name is provided, Select synchronizes the selected value to a native hidden input for form submission and reset behavior.

Customization

  • Select exposes root, trigger, value, content, viewport, group, label, item, indicator, separator, scroll button, and hidden input hooks through data-radcn-select* attributes.
  • Trigger widths such as w-[180px] and w-[280px] map to class, style, CSS variables, or app CSS rather than Tailwind utilities.
  • Fruit options, timezone groups, option labels, submitted values, and selected display text remain app-owned data rendered through RadCN item primitives.
  • Scroll buttons, viewport height, popper placement, custom classes, styles, and tokens are explicit RadCN props or app CSS extension points.

Remix 3 Notes

  • React props, Radix SelectPrimitive, className, data-slot, Tailwind utilities, cn, and vendor source map to explicit RadCN props, class, public data hooks, package CSS, inline style, and CSS variables.
  • CheckIcon, ChevronDownIcon, ChevronUpIcon, and lucide-react are presentation choices; RadCN provides dependency-free indicators and trigger glyphs.
  • Portal behavior is supported through SelectPortal when an app needs it, but the named docs examples keep content scoped for visible documentation evidence.
  • React state and onValueChange map to package-owned browser enhancement, native hidden values, route state, or app-owned event handling.
  • vendor source remains read-only evidence and is not imported by RadCN.