NumberInput

A field for a number. A text input - so what is typed is the user's until it is settled - that reads and writes the number in its locale, clamps it to a range, and steps by the arrow keys, Home/End,...

Demos

For usage examples, visit the component demo page: NumberInput

Import

import { NumberInput } from '@soroush.tech/design-system/NumberInput'

Props

PropTypeDefaultDescription
valuenumber | null-The number - null for none. Controlled with onChange.
defaultValuenumber | nullnullThe number at first, when uncontrolled.
onChange(value: number | null) => void-Fired as the number changes - as it is typed, stepped or cleared.
onChangeCommitted(value: number | null) => void-Fired when the number is settled: on blur, Enter, a step or Home/End.
min / maxnumber-The range. What is typed or stepped is clamped to it when settled.
step / smallStep / largeStepnumber1 / 0.1 / 10What the arrow keys and buttons step by; with Alt held; with Shift held.
formatIntl.NumberFormatOptionsevery digit, ungroupedHow the number is shown - { style: 'percent' }, { minimumFractionDigits: 2 }...
localestringthe document'sHow the number is written and read.
shouldSnapOnStepbooleanfalseA step lands on the grid of step, counted from min (or 0).
shouldScrubOnWheelbooleanfalseThe wheel steps the number while the field is focused.
spinButtons'stacked' | 'sides' | 'none''stacked'The step buttons: an up and a down at the end, a minus and a plus either side, or none.
incrementLabel / decrementLabelstring-Name the buttons - no built-in text.
startContent / endContentReactNode-Content inside the frame - an icon before the number; a unit such as px, % or a button after it, before the step buttons. Each has the room at the frame's edge the input has on its side.
variant'default' | 'outlined' | 'underline' | 'text'theme.defaults.inputVariantThe frame, as on TextInput.
sizekeyof Theme['sizes']its FormControl's, 'md'Padding and font size.
colorkeyof Theme['palette']its FormControl's, 'primary'The focus border and button hover.
textColorkeyof Theme['text']its FormControl's, color's mainThe number's color.
error / disabled / requiredbooleanits FormControl'sThe error border and aria-invalid; off; marked required.
readOnlybooleanfalseTakes no change: the keys, the wheel and the buttons do nothing.
fullWidthbooleanits FormControl'sFills its container.
namestring-What a form submits the raw number as.
idstringits FormControl'sOn the field, which a FormLabel targets.
placeholder, autoFocus, aria-label...On the field.

It takes the styled-system space props, width, minWidth, maxWidth, className and data-testid too.

Typing and settling

What is typed is the user's: 12. stays as typed while it is told as 12, and text that is not yet a number - a lone minus - waits to be finished. The number is settled on blur or Enter: it is written again in its format, clamped to min/max, and onChangeCommitted is told. Text that never became a number settles back to the last one; empty text settles to null.

A locale's digits and decimal mark are read as well as written - 1,5 in de-DE, ١٢٫٥ in ar-EG - and a percent format reads 50% as 0.5.

Keys, the wheel and the buttons

KeyDoes
ArrowUp / ArrowDownStep by step; with Alt by smallStep; with Shift by largeStep.
Home / EndGo to min / max - when there is one.
EnterSettle what is typed.

Steps add without float noise (0.1 + 0.2 is 0.3), start from 0 when there is no number, and stop at the range. The buttons step on a press and repeat while held, after a pause; they are out of the tab order, since the keys already step, and are off at the range's ends.

In a FormControl

Its FormLabel targets the field, its FormHelperText describes it, and the field's size, color, error, disabled and required apply, an own prop winning.

<FormControl>
  <FormLabel>Quantity</FormLabel>
  <NumberInput min={1} max={99} incrementLabel="Increase" decrementLabel="Decrease" />
  <FormHelperText>Between 1 and 99.</FormHelperText>
</FormControl>

A number written with its unit

What is typed is read for its digits, in the field's locale, so 12px, 12 px and 1.5rem are 12, 12 and 1.5 - a pasted CSS length is a number. Put the unit where it is shown in endContent.

Customization

theme.components.NumberInput styles the root, the input, the content before and after it, the spinButtons pair and each increment / decrement button. Its defaultProps set variant, size, color, spinButtons, the three steps, shouldSnapOnStep, shouldScrubOnWheel - and the two button labels, which give every field the app's language.

Source code

If this page does not answer your question, have a look at the implementation of the component for more detail.