Number inputs
Examples
Section titled “Examples”<div class="number-input"> <button type="button" class="number-input-step" aria-label="Decrease" onclick="this.parentElement.querySelector('input').stepDown()" > <i class="ti ti-minus" aria-hidden="true"></i> </button> <input class="number-input-field" type="number" value="3" min="0" max="10" aria-label="Quantity" /> <button type="button" class="number-input-step" aria-label="Increase" onclick="this.parentElement.querySelector('input').stepUp()" > <i class="ti ti-plus" aria-hidden="true"></i> </button></div><NumberInput defaultValue={3} min={0} max={10} inputAriaLabel="Quantity" />Step and range
Section titled “Step and range”<div class="number-input"> <button type="button" class="number-input-step" aria-label="Decrease" onclick="this.parentElement.querySelector('input').stepDown()" > <i class="ti ti-minus" aria-hidden="true"></i> </button> <input class="number-input-field" type="number" value="50" min="0" max="100" step="5" aria-label="Threshold" /> <button type="button" class="number-input-step" aria-label="Increase" onclick="this.parentElement.querySelector('input').stepUp()" > <i class="ti ti-plus" aria-hidden="true"></i> </button></div><NumberInput defaultValue={50} min={0} max={100} step={5} inputAriaLabel="Threshold"/><div class="number-input number-input-sm"> <button type="button" class="number-input-step" aria-label="Decrease"> <i class="ti ti-minus" aria-hidden="true"></i> </button> <input class="number-input-field" type="number" value="1" aria-label="Small" /> <button type="button" class="number-input-step" aria-label="Increase"> <i class="ti ti-plus" aria-hidden="true"></i> </button></div><div class="number-input"> <button type="button" class="number-input-step" aria-label="Decrease"> <i class="ti ti-minus" aria-hidden="true"></i> </button> <input class="number-input-field" type="number" value="1" aria-label="Medium" /> <button type="button" class="number-input-step" aria-label="Increase"> <i class="ti ti-plus" aria-hidden="true"></i> </button></div><div class="number-input number-input-lg"> <button type="button" class="number-input-step" aria-label="Decrease"> <i class="ti ti-minus" aria-hidden="true"></i> </button> <input class="number-input-field" type="number" value="1" aria-label="Large" /> <button type="button" class="number-input-step" aria-label="Increase"> <i class="ti ti-plus" aria-hidden="true"></i> </button></div><NumberInput size="sm" defaultValue={1} inputAriaLabel="Small" /><NumberInput defaultValue={1} inputAriaLabel="Medium" /><NumberInput size="lg" defaultValue={1} inputAriaLabel="Large" />Formatting React only
Section titled “Formatting ”<NumberInput defaultValue={1499} min={0} step={1} format={{ style: "currency", currency: "USD" }} inputAriaLabel="Price"/>Reference
Section titled “Reference”| Prop | Type | Default |
|---|---|---|
size |
"sm" | "md" | "lg" |
"md" |
placeholder |
string |
— |
inputAriaLabel |
string |
— |
decrementLabel |
string |
"Decrease" |
incrementLabel |
string |
"Increase" |
decrementIcon |
ReactNode |
− glyph |
incrementIcon |
ReactNode |
+ glyph |
classNames |
slots | — |
Renders the whole group — steppers, field, ARIA — from one component, so there are no sub-parts to compose; classNames covers group, decrement, input, increment. inputAriaLabel names the field when there’s no associated <label>; inside a Field the label supplies the name instead.
Wraps Base UI NumberField, which owns value / defaultValue / onValueChange, min, max, step, format, clamp-on-blur, and scrub-to-change. format takes Intl.NumberFormat options.
format is React-only: a formatted 1,000 is not a valid type="number" value, so displaying it means splitting display from value, which needs the wrapper. The vanilla bundle gets native stepping and constraint validation, but not grouped thousands.
Vanilla
Section titled “Vanilla”| Class | Effect |
|---|---|
number-input |
Connected − / field / + group: bordered 0.5rem-radius shell, focus ring on :focus-within |
number-input-field |
Borderless field inside it: right-aligned tabular digits, native spinners hidden |
number-input-step |
2rem-wide stepper button, divided from the field, hover tint |
number-input-sm |
text-xs field, 1.75rem steppers |
number-input-lg |
text-base field, 2.25rem steppers |
number-input-root |
display: contents — the React wrapper element, invisible to layout |
There is no number-input-md — it’s the unmodified number-input. Step buttons in vanilla call the platform stepUp() / stepDown(), which also honour min, max and step; each needs its own aria-label, and the field needs one too unless a <label> is associated. Native spinners are hidden in both engines, so the visible steppers are the only affordance. Digits are right-aligned and tabular so a column of values lines up.