How to build an accessible NumberInput in React
Exposes role="spinbutton" with aria-valuenow/min/max so assistive tech announces the current value and bounds; stepper buttons are taken out of the tab order (tabIndex=-1) and labeled, keeping keyboard focus on the field while ArrowUp/ArrowDown drive stepping.
When to use a NumberInput
- Collecting a bounded numeric value where increment/decrement by a known step is natural (quantity, opacity, count)
- Numeric entry that needs clamping to min/max, fixed precision, or locale-aware display formatting (e.g. currency)
- Inputs where keyboard ArrowUp/ArrowDown stepping speeds up adjustment
When not to use it
- Free-form or non-numeric text — use Input
- Choosing along a continuous range where the exact number matters less than the relative position — use Slider
- Phone numbers, PINs, or codes that are digit strings rather than quantities — use Input or OtpInput
Keyboard interactions
Role spinbutton, verified at WCAG 2.2-AA.
ArrowUpArrowDownEnterTab
Common mistakes
Avoid: <Input type="number" onChange={...} />
Prefer: <NumberInput min={0} max={99} step={1} onChange={...} />
NumberInput adds stepper buttons, clamping, precision rounding, and Intl formatting that a raw number input does not, and reports a parsed number | null rather than a string
Example
<NumberInput label="Quantity" defaultValue={1} min={0} max={99} />