Number field with steppers on both sides or stacked on the right, min/max/step, full keyboard control and press-and-hold that speeds up; digits roll to each new value on a spring.
numberstepperspinbuttonquantityinputformcounter
Installation
$npx shadcn@latest add @geturui/number-input
Also installs: Field
Usage
usage.tsx
1import * as React from "react";
2import { NumberInput } from "@/components/ui/number-input";
3
4export function Seats() {
5 const [seats, setSeats] = React.useState<number | null>(5);
6 return <NumberInput label="Seats" value={seats} onValueChange={setSeats} min={1} max={250} unit="seats" />;
7}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | — | Value when controlled. null means empty. |
defaultValue | number | null | null | Initial value when uncontrolled. |
onValueChange | (value: number | null) => void | — | Called on every step, key press or committed edit. |
min / max | number | — | Bounds. Values clamp on commit and the matching stepper disables at the edge. |
step | number | 1 | Amount per arrow press or stepper tap; its decimals set the precision. |
largeStep | number | step × 10 | Amount for PageUp/PageDown and Shift+Arrow. |
orientation | "horizontal" | "vertical" | "horizontal" | Minus/plus on both sides, or chevrons stacked on the right. |
size | "sm" | "md" | "lg" | "md" | 32, 40 or 48px, matching Button. |
unit | string | — | Unit shown after the value and read out with it. |
formatOptions | Intl.NumberFormatOptions | — | Formatting, e.g. currency or percent. |
locale | string | "en-US" | Formatting locale. Fixed by default so server and client agree. |
label / hint / error | ReactNode | — | Field label, helper line and error message. |
Accessibility
- The input has role="spinbutton" with aria-valuenow, aria-valuemin, aria-valuemax and aria-valuetext including the unit.
- Arrow Up/Down step, Shift or PageUp/PageDown take large steps, Home/End jump to min/max, Escape cancels a typed edit and Enter commits it.
- Steppers are labelled buttons kept out of the tab order (the keyboard already covers them); the rolling digits are aria-hidden and snap when reduced motion is on.