Number input
Numeric field with BDS-styled increment and decrement steppers. Reach for it when a value is entered by number and nudged up or down — quantities, counts, durations.
NumberInput is TextInput forced to type="number" with the native browser spinners hidden and replaced by BDS caret buttons. It respects min, max, and step, and works identically in controlled and uncontrolled modes. Reach for it whenever a value is typed as a number and adjusted by small increments — a quantity on a line item, a count, a duration.
Use it for
- Quantity and count fields ("Qty", "Seats", "Rooms")
- Bounded numeric entry where
min/maxguard the range - Values nudged by a fixed
step(increments of 1, 5, 0.25) - Any numeric input that benefits from click-to-step alongside typing
Import
import { NumberInput } from '@brikdesigns/bds';Variants
Default
The stepper buttons are always present. Clicking increment or decrement fires onChange with the clamped next value, so controlled and uncontrolled modes behave identically.
// Uncontrolled
<NumberInput label="Quantity" defaultValue={1} min={0} max={99} step={1} />
// Controlled
<NumberInput
label="Quantity"
value={qty}
onChange={(e) => setQty(Number(e.target.value))}
min={0}
max={99}
/>Sizes
sm (32px), md (40px, default), lg (48px) — inherited from TextInput.
<NumberInput label="Compact" size="sm" defaultValue={1} />
<NumberInput label="Default" size="md" defaultValue={1} />
<NumberInput label="Prominent" size="lg" defaultValue={1} />Bounds and step
min, max, and step are forwarded to the native input and enforced by the steppers — clicking never pushes the value past min / max, and each click moves by step.
<NumberInput label="Rating" defaultValue={2.5} min={0} max={5} step={0.5} />Error and helper text
Inherited from TextInput. error triggers aria-invalid and replaces helperText.
<NumberInput label="Seats" defaultValue={0} min={1} error="At least one seat is required" />
<NumberInput label="Seats" defaultValue={2} min={1} helperText="Between 1 and 8" />When not to use
Don't use NumberInput for a plain text, email, tel, or url field. Those are TextInput directly — NumberInput's steppers and type="number" are wrong for non-numeric entry.
- Don't use it for a standalone
+/−counter with no typed value — that is Stepper. - Don't use it for a bounded value picked on a track. A range chosen by dragging is Slider.
Accessibility
- The steppers are
<button type="button">witharia-label"Increment" / "Decrement" andtabIndex={-1}— they are reachable by pointer, and keyboard users step the value with the nativeArrowUp/ArrowDownon the focused input. - Always pass
label— it becomes the accessible name for the field. type="number"is reserved by NumberInput and cannot be overridden.
API
| Prop | Type | Default |
|---|---|---|
min | number | — |
max | number | — |
step | number | 1 |
Inherits all TextInput props except type (forced to "number") and iconAfter (reserved for the steppers) — including label, size, error, helperText, fullWidth, and disabled.
Related
- TextInput — the base field NumberInput wraps
- Stepper — standalone increment / decrement counter with no typed value
- Slider — bounded value chosen on a track
- Storybook playground