Select
StableThe browser’s own select in the field finish, with an up-down chevron, so phones show their own picker and the keys work for free.
A native <select> wrapped with the chevron icon. Phones show their own picker, desktops get type-to-find and arrow keys, and option groups work as usual. A disabled select fades to half strength, chevron and all. Takes every <select> attribute except size. For long lists people search, use Combobox instead.
Updated View source
Calendar settings
import { useState } from 'react'import { Field, Select } from '@rchursin/product/ui'
/** * The browser's own select in the field finish: the native list on phones, type-to-find and * arrow keys on desktop. Option groups and disabled selects work as usual. */export default function SelectSettings() { const [view, setView] = useState('week') const [weekStart, setWeekStart] = useState('sunday') const [zone, setZone] = useState('America/Los_Angeles')
return ( <div className="grid w-full max-w-lg grid-cols-1 gap-5 sm:grid-cols-2"> <Field label="Open the calendar on"> {(p) => ( <Select {...p} name="view" value={view} onChange={(e) => setView(e.target.value)}> <option value="day">Day</option> <option value="week">Week</option> <option value="month">Month</option>Usage
import { Select, type SelectProps } from '@rchursin/product/ui'<Field label="Week starts on"> {(p) => ( <Select {...p} value={weekStart} onChange={(e) => setWeekStart(e.target.value)}> <option value="sunday">Sunday</option> <option value="monday">Monday</option> </Select> )}</Field>Examples
A choice is required
An empty first option, so nobody accepts a guess. Press Send without choosing: the error appears and focus moves to the select.
import { useRef, useState, type FormEvent } from 'react'import { Button, Field, Select } from '@rchursin/product/ui'
/** * An empty first option makes people choose instead of accepting a guess. Press Send without * choosing: the error appears and focus moves to the select. The large size, for a short form. */export default function SelectRequired() { const select = useRef<HTMLSelectElement>(null) const [topic, setTopic] = useState('') const [tried, setTried] = useState(false) const [sent, setSent] = useState(false) const error = tried && !topic ? 'Choose what your message is about' : undefined
function send(e: FormEvent) { e.preventDefault() // a sample: nothing leaves the page setTried(true) if (!topic) return select.current?.focus() setSent(true) }API
Select props
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'md' | 'lg' | 'md' | Height 32 or 40 pixels, matching TextInput. Replaces the native size attribute. |
children | ReactNode | – | <option> and <optgroup> elements. |
className | string | – | Classes on the wrapper <div> that holds the select and the chevron, not on the select. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| SpaceAltDown arrow | Opens the list. |
| Up arrowDown arrow | Move to the previous or next option. On macOS they open the list first. |
| A–Z | Jumps to the first option that starts with what you type. |
| Enter | In an open list, chooses the option and closes it. |
| Esc | Closes the list without changing the choice. |
- A real <select>, so screen readers announce it as a combo box with its value, and the label from Field names it.
- The chevron icon is aria-hidden and ignores the pointer, so clicks reach the select underneath.
- The ref reaches the <select> itself (not the wrapper), so you can move focus to it, as the second example does after a failed submit.
Guidelines
Do
- Use it for 4 to 15 known options.
- Order options in a way people expect: by time, by size or alphabetically.
- Start with an empty option when there is no safe default.
Don’t
- Don’t use it for two or three options: show them all with RadioGroup or Segmented.
- Don’t use it for long lists people would rather search: use Combobox.
In use
Related
- ComboboxA text field that suggests options as you type, for long lists like time zones or skills (APG list autocomplete).
- Radio groupA fieldset of native radios, each a ring with the brand square inside when chosen, with optional hints.
- PickerA filterable list of options, on its own or in a popover: type to filter, arrows and Enter to choose, or 1–9 to pick at once.
- FieldLabel above, hint under it, error in words between the hint and the control, with the ids and aria wiring done for you.
Source
The component’s own files, exactly as they run on this site.
/** * Form fields. Labels sit above inputs, hints under the label, errors between the hint and the * input with an icon and the word "Error:" for screen readers (never colour alone). * Optional fields say "(optional)"; required ones carry no mark. * * <Field label="Email" hint="We send the sign-in code here." error={err}> * {(p) => <TextInput {...p} type="email" autoComplete="email" />} * </Field> */import { forwardRef, useId, useLayoutEffect, useRef, useState, type InputHTMLAttributes, type KeyboardEvent, type ReactNode, type SelectHTMLAttributes, type TextareaHTMLAttributes,