Select

A native dropdown for choosing one option from a list. Extends the platform's built-in select with consistent sizing, labels, and error states.

Preview

Sizes

SizeHeightFont sizePadding
sm32px12px8px 10px
md40px13px10px 12px
lg48px15px12px 16px

States

This field is required

Implementation

Select.tsx
tsx
import { Select } from "@/components/ui/Select";
import { useState } from "react";

// Basic with placeholder
<Select label="Framework" placeholder="Choose a framework">
  <option value="next">Next.js</option>
  <option value="remix">Remix</option>
  <option value="astro">Astro</option>
</Select>

// Controlled
const [region, setRegion] = useState("us-west");
<Select
  label="Region"
  value={region}
  onChange={(e) => setRegion(e.target.value)}
>
  <option value="us-east">US East</option>
  <option value="us-west">US West</option>
  <option value="eu">Europe</option>
</Select>

// Error state
<Select label="Status" error="Please select a status">
  <option value="">Select status</option>
  <option value="active">Active</option>
  <option value="inactive">Inactive</option>
</Select>

Props

All native <select> attributes are forwarded. Custom props are listed below.

PropTypeDefaultDescription
labelstringVisible label rendered above the select.
placeholderstringPlaceholder option shown when no value is selected.
helperTextstringSupplemental hint rendered below the select.
errorstringError message. Replaces helperText and applies error styling.
size"sm" | "md" | "lg""md"Controls height, padding, and font size.

ARIA roles

ElementRoleKey attributes
Triggercomboboxaria-expanded, aria-haspopup='listbox', aria-labelledby
Dropdownlistboxaria-label
Optionoptionaria-selected

Keyboard

KeyAction
Space / EnterOpen the dropdown
Arrow Down / UpNavigate between options (also opens if closed)
EnterSelect the focused option and close
EscapeClose the dropdown without selecting
Home / EndJump to the first / last option
TabClose and move focus to the next element

Accessibility

  • Uses a native <select> element — fully keyboard navigable and compatible with all assistive technology.
  • The label prop renders a <label> element properly associated via htmlFor.
  • Error messages are surfaced as visible text; pair with aria-describedby for screen readers.
  • Disabled state is communicated natively via the disabled attribute.
  • ChevronDown overlay uses pointer-events:none so it does not interfere with the native control.