Select

Native select with label, hint, and error wiring.

organismsSource
import { Select } from '@elirobinson/react/components/organisms/Select';

Styles: @elirobinson/react/styles/atoms/field.css — already included when you import @elirobinson/react/styles.css.

Show code
import { Select } from '@elirobinson/react/components/organisms/Select';

export default function Basic() {
  return (
    <Select label="Sport" defaultValue="">
      <option value="">Choose a sport</option>
      <option value="football">Football</option>
      <option value="basketball">Basketball</option>
      <option value="rugby">Rugby</option>
      <option value="netball">Netball</option>
    </Select>
  );
}

When to use it

Use Select when the options are plain text and few enough to scan — roughly under twenty. It renders a native <select>, so you get the platform's picker, keyboard support, and typeahead for free, on every device, without a line of extra code.

Reach for Combobox instead once the list needs search-as-you-type, or an option needs more than a label — an icon, a description, a secondary line. Reach for RadioGroup when there are only two to four choices and showing them all at once beats hiding them behind a click.

Hint, error, and disabled

hint and error share one line under the field — passing both shows only the error, with aria-invalid set on the <select> and the message announced immediately.

Filter guides by sport.
Choose a sport to see matching guides.
Show code
import { Select } from '@elirobinson/react/components/organisms/Select';

export default function States() {
  return (
    <div className="demo-col">
      <Select label="Sport" hint="Filter guides by sport." defaultValue="">
        <option value="">Choose a sport</option>
        <option value="football">Football</option>
        <option value="basketball">Basketball</option>
        <option value="rugby">Rugby</option>
      </Select>
      <Select label="Sport" error="Choose a sport to see matching guides." defaultValue="">
        <option value="">Choose a sport</option>
        <option value="football">Football</option>
        <option value="basketball">Basketball</option>
        <option value="rugby">Rugby</option>
      </Select>
      <Select label="Sport" disabled defaultValue="football">
        <option value="football">Football</option>
        <option value="basketball">Basketball</option>
      </Select>
    </div>
  );
}

Props

PropTypeDefaultDescription
labelrequiredstring
errorstring
hintstring

Also accepts all SelectHTMLAttributes<HTMLSelectElement> props.

Accessibility

  • Renders a native <select> wired to a real <label> via htmlFor/id — no ARIA substitute needed for the label relationship.
  • error sets aria-invalid="true" on the <select> and renders with role="alert", so assistive tech announces it as soon as it appears. It also takes over the description slot from hint — the two never show at once.
  • hint and error are wired through aria-describedby, merged with any aria-describedby you pass in yourself rather than overwriting it.
  • Keyboard: entirely native. Tab focuses the control, Enter/Space/Down Arrow open the option list, arrow keys move through it, and typing jumps to a matching option. None of this is reimplemented — it's the browser's own <select> behavior, which is why it also gets the OS-native picker UI on mobile for free.
  • The ref forwards to the underlying <select> element.

Do

  • Pass a real label string every time — it is required, and renders as a genuine <label>.
  • Use hint for supporting context and error for validation — error automatically replaces the hint so they never stack.
  • Trust the native option list for anything under about twenty items.
  • Read the value off the native change event, the same as any uncontrolled <select>.

Don't

  • Reach for Select when options need icons, descriptions, or search — use Combobox instead.
  • Build a custom-styled dropdown on top of a hidden <select> — the native picker is the accessible one.
  • Rely on placeholder-only labelling; an empty or missing label leaves the field unannounced.
  • Show a validation error without also setting the field to error — the message and the state should move together.