Default
An input for numerical values.
Editable example
() => {
const [value, setValue] = React.useState(1);
return (
<NumberInput label="Basic number input" value={value} onChange={setValue} />
);
}
Best practices
- It's preferable to use a
NumberInput
over a <TextInput type="number" />
for consistency of ergonomics when inputting numbers. - Provide a visual
label
unless the purpose of the input is communicated or implied from its context, in which case a label still must be provided but can be hidden visually using the labelHidden
prop. - Avoid mixing and matching sizes. If a medium sized
NumberInput
is used, don't use a large SegmentedControl
next to it.
Format and parse value
Define how the number is formatted and parsed in the input, useful for cases like currency inputs. You can use the formatValue
prop to define how the number gets formatted into a string within the input. If you need to parse the internal value back to a number in a particular way different to the default you can use the parseValue
prop as well.
Editable example
() => {
const [value, setValue] = React.useState(1);
return (
<NumberInput
label="Price"
value={value}
onChange={setValue}
formatValue={(value) => new Intl.NumberFormat("en", { style: "currency", currency: "EUR" }).format(value)}
/>
);
}
Min and max
Use the error
prop to communicate invalid inputs to the user. Avoid displaying validation as the user is typing. The preferred interaction is to validate on blur if the user has typed something. Any errors should also be displayed on inputs when a form is attempted to be submitted.
Editable example
() => {
const [value1, setValue1] = React.useState(1);
const [value2, setValue2] = React.useState(5);
const [value3, setValue3] = React.useState(1);
return (
<>
<NumberInput label="Min" min={1} value={value1} onChange={setValue1} />
<NumberInput label="Max" max={5} value={value2} onChange={setValue2} />
<NumberInput label="Min+Max" min={1} max={5} value={value3} onChange={setValue3} />
</>
);
}
Step and snap
Define how much to increment by, and whether to snap to the nearest multiple of that increment with step
and snap
props. By default, number inputs will step by 1.
Editable example
() => {
const [value1, setValue1] = React.useState(1);
const [value2, setValue2] = React.useState(5);
return (
<>
<NumberInput label="Step by 0.1" step={0.1} value={value1} onChange={setValue1} />
<NumberInput label="Step by 5 & snap" step={5} snap value={value2} onChange={setValue2} />
</>
);
}
Hide stepper
Hide the stepper arrow buttons.
Editable example
() => {
const [value, setValue] = React.useState(1);
return (
<NumberInput hideStepper label="No stepper" value={value} onChange={setValue} />
);
}
Label
Use a label
to indicate what the number is for e.g. Quantity
, Price
etc. If the purpose of the input is communicated by its context, the labelHidden
prop can be used to hide it visually but keep it accessible by assistive technology.
Editable example
() => {
const [quantity, setQuantity] = React.useState(1);
const [expiry, setExpiry] = React.useState(2);
return (
<>
<NumberInput label="Quantity" value={quantity} onChange={setQuantity} />
<Stack direction="horizontal" align="center" gap="xs">
<Text>Page will expire</Text>
<NumberInput autoGrow labelHidden min={1} label="Days until expiry" value={expiry} onChange={setExpiry} />
<Text>days after set live</Text>
</Stack>
</>
);
}
Size
Use larger sizes for forms and smaller sizes for contextual or compact interfaces. Typically all inputs in a given context should all share a consistent size between them.
Editable example
() => {
const [value, setValue] = React.useState(1);
return (
<>
<NumberInput value={value} onChange={setValue} size="xs" label="XSmall" />
<NumberInput value={value} onChange={setValue} size="s" label="Small" />
<NumberInput value={value} onChange={setValue} size="m" label="Medium" />
<NumberInput value={value} onChange={setValue} size="l" label="Large" />
</>
);
}
Theme
Use the dark theme when placed on a dark surface such as a dark popover or card.
Editable example
() => {
const [value, setValue] = React.useState(1);
return (
<Card theme={CardTheme.Dark}>
<Stack padding="s" className={darkTheme}>
<NumberInput label="Dark number input" value={value} onChange={setValue} />
</Stack>
</Card>
);
}
Start/end elements
The startElement
and endElement
props take any ReactNode
. This is useful for placing an icon or button at the start/end of an input. The size of the element will automatically adjust the padding inside the input so that the text won't be overlapped by the elements.
Editable example
() => {
const [value1, setValue1] = React.useState(1);
const [value2, setValue2] = React.useState(1);
const [value3, setValue3] = React.useState(1);
return (
<>
<NumberInput startElement={<Edit />} label="Start element" value={value1} onChange={setValue1} />
<NumberInput endElement={<IconButton icon={<Tick />} size={ButtonSize.Small} />} label="End element" value={value2} onChange={setValue2} />
<NumberInput startElement={<Edit />} endElement={<IconButton icon={<Tick />} size={ButtonSize.Small} />} label="Both sides" value={value3} onChange={setValue3} />
</>
);
}
Variants
In most cases the default bordered input should be used. For standalone inputs that are part of smaller or tightly packed components (such as an image card overlay or within a navbar) the Underlined
variant works best. Use the Borderless
variant in areas where the input needs to seamlessly fit within another component, for example an input that sits within a Toolbar
.
Editable example
() => {
const [value1, setValue1] = React.useState(1);
const [value2, setValue2] = React.useState(1);
const [value3, setValue3] = React.useState(1);
const [value4, setValue4] = React.useState(1);
return (
<>
<NumberInput variant="bordered" label="Bordered" value={value1} onChange={setValue1} />
<NumberInput variant="underlined" label="Underlined" value={value2} onChange={setValue2} />
<NumberInput variant="semiborderless" label="Semiborderless" value={value3} onChange={setValue3} />
<NumberInput variant="borderless" label="Borderless" value={value4} onChange={setValue4} />
</>
);
}
Disabled
Use the disabled
prop to communicate that an input exists, but cannot be interacted with.
Editable example
() => {
const [value, setValue] = React.useState(1);
return (
<NumberInput disabled label="Disabled number input" value={value} onChange={setValue} />
);
}
Accessibility
- Always provide a descriptive label even when using the
labelHidden
prop. This will be read by screen readers when the input is focused even when it's not visibly shown.