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,...
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, the mouse wheel and buttons that repeat while held. It is a role="spinbutton", so the
number and its range are announced, and a hidden input carries the raw number to a form.
Every label is yours - the buttons' (incrementLabel, decrementLabel) - so the field carries no
language of its own.
import { NumberInput } from '@soroush.tech/design-system/NumberInput'
;<NumberInput
aria-label="Quantity"
value={quantity}
onChange={setQuantity}
min={1}
max={99}
incrementLabel="Increase"
decrementLabel="Decrease"
/>
Examples
Default
const args: Partial<NumberInputProps> = { defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <NumberInput key={String(args.defaultValue)} {...args} />
With Content
px
const args: Partial<NumberInputProps> = { defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <NumberInput {...args} variant="outlined" defaultValue={12} startContent={<Icon name="add" size="sm" color="inherit" />} endContent={<span>px</span>} />
Spin Buttons On The Sides
const args: Partial<NumberInputProps> = { defaultValue: 5, min: 0, max: 10, spinButtons: 'sides', incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <NumberInput key={String(args.defaultValue)} {...args} />
No Spin Buttons
const args: Partial<NumberInputProps> = { defaultValue: 5, min: 0, max: 10, spinButtons: 'none', incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <NumberInput key={String(args.defaultValue)} {...args} />
Controlled Value
Value: 5
const args: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <Controlled {...args} />
Change And Commit
const args: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <Events {...args} />
Formats
const args: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <Flex gap={2}> <NumberInput {...args} defaultValue={1234.5} min={undefined} max={undefined} aria-label="Plain" /> <NumberInput {...args} defaultValue={1234.5} min={undefined} max={undefined} locale="de-DE" format={{ useGrouping: true }} aria-label="German" /> <NumberInput {...args} defaultValue={0.25} min={0} max={1} step={0.05} format={{ style: 'percent' }} aria-label="Percent" /> <NumberInput {...args} defaultValue={19.9} min={0} max={undefined} locale="en-US" format={{ style: 'currency', currency: 'USD' }} aria-label="Price" /> <NumberInput {...args} defaultValue={12.5} min={undefined} max={undefined} locale="ar-EG" aria-label="Arabic digits" /> </Flex>
Snap To Step
const args: Partial<NumberInputProps> = { defaultValue: 7, min: 0, max: 100, step: 5, shouldSnapOnStep: true, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <NumberInput key={String(args.defaultValue)} {...args} />
Scrub With The Wheel
const args: Partial<NumberInputProps> = { defaultValue: 5, min: 0, max: 10, shouldScrubOnWheel: true, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <NumberInput key={String(args.defaultValue)} {...args} />
Variants
const args: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <Flex gap={2}> {numberInputVariantTokens.map((variant) => ( <NumberInput key={variant} {...args} variant={variant} aria-label={`Quantity, ${variant}`} /> ))} </Flex>
Sizes
const args: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <Flex gap={2}> {(['sm', 'md', 'lg'] as const).map((size) => ( <NumberInput key={size} {...args} size={size} aria-label={`Quantity, ${size}`} /> ))} </Flex>
States
const args: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <Flex gap={2}> <NumberInput {...args} error aria-label="Quantity, in error" /> <NumberInput {...args} disabled aria-label="Quantity, disabled" /> <NumberInput {...args} readOnly aria-label="Quantity, read-only" /> </Flex>
In Form Control
Between 1 and 99.
const { error, disabled, required, size, color, fullWidth }: Partial<NumberInputProps> = { 'aria-label': 'Quantity', defaultValue: 5, min: 0, max: 10, incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', } <FormControl error={error} disabled={disabled} required={required} size={size} color={color} fullWidth={fullWidth} > <FormLabel>Quantity</FormLabel> <NumberInput variant="outlined" min={1} max={99} defaultValue={1} incrementLabel="Increase" decrementLabel="Decrease" /> <FormHelperText>{error ? 'Choose between 1 and 99.' : 'Between 1 and 99.'}</FormHelperText> </FormControl>
Right To Left
const args: Partial<NumberInputProps> = { defaultValue: 5, min: 0, max: 10, spinButtons: 'sides', incrementLabel: 'Increase', decrementLabel: 'Decrease', variant: 'outlined', 'aria-label': 'Quantity', } <View dir="rtl"> <NumberInput {...args} locale="ar-EG" /> </View>
Customized
<ThemeProvider theme={customized}> <NumberInput aria-label="Quantité" defaultValue={10} min={0} max={100} /> </ThemeProvider>