NumberInput
A form input for numeric values with built-in validation, min/max constraints, and step controls. Use NumberInput for quantities, measurements, percentages, and similar inputs.
Installation
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
Requires React 19+, base CSS and a Theme provider. See Setup.
Number Input
A form input for numeric values with built-in validation, min/max constraints, and step controls. Use NumberInput for quantities, measurements, percentages, and similar inputs.
| Prop | Type | Default | Description |
|---|---|---|---|
| label * | string | Label text for the input (always rendered for accessibility). | |
| value * | number | null | undefined | Current value of the input. | |
| onChange * | (value: number) => void | Callback fired when a valid text edit commits on blur or Enter, or when a step or clear control changes the value. | |
| size | 'sm' | 'md' | 'lg' | 'md' | Size variant. |
| isLabelHidden | boolean | Visually hide the label (still accessible to screen readers). | |
| description | string | Description text displayed between the label and input. | |
| isOptional | boolean | Whether the field is optional (mutually exclusive with isRequired). | |
| onKeyDown | (e: KeyboardEvent<HTMLInputElement>) => void | Callback fired on keydown events on the input. | |
| isRequired | boolean | Whether the field is required (mutually exclusive with isOptional). | |
| isDisabled | boolean | Whether the input is disabled. | |
| isReadOnly | boolean | false | Makes the input read-only: the value is shown at full opacity and still submits with the form, but cannot be edited. Unlike isDisabled, a read-only input is not dimmed and stays in the tab order. Stepping is off in every form while read-only: arrow keys, the wheel, and the number steppers. isDisabled takes precedence when both are set. |
| disabledMessage | string | Explains why the input is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the input focusable via aria-disabled (the field becomes read-only). Use this instead of wrapping a disabled NumberInput in Tooltip. | |
| placeholder | string | Placeholder text. | |
| labelTooltip | string | Tooltip text to display in an info icon at the end of the label. | |
| startIcon | IconType | Icon to display at the start of the input. See astryx docs icons for valid semantic names. | |
| labelIcon | IconType | Icon to display before the label text. See astryx docs icons for valid semantic names. | |
| status | {type: 'error' | 'warning' | 'success', message?: string} | Validation status with optional message. | |
| statusVariant | 'attached' | 'detached' | 'tooltip' | 'attached' | How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing; tooltip hides the message box and surfaces it in a tooltip on the status icon. |
| min | number | null | Minimum value allowed. A smaller entry commits at this value on blur or Enter. | |
| max | number | null | Maximum value allowed. A larger entry commits at this value on blur or Enter. | |
| step | number | null | 1 | Step increment for the input. |
| formatValue | (value: number) => string | Formats the committed value while the input is not focused. The raw numeric value is shown on focus for editing and the formatted value is exposed through aria-valuetext. | |
| isWheelEnabled | boolean | true | Whether scrolling the wheel over the focused input steps the value. Disable this when page scrolling should always take priority. |
| hasNumberSteppers | boolean | false | Shows increment and decrement buttons at the end of the input. |
| units | string | null | Units text to display at the end of the input (e.g., "%" or "GB"). | |
| isIntegerOnly | boolean | Only allow integer values (no floating point). | |
| hasClear | boolean | false | Shows a clear (×) button when the input has a value. When true, the onChange callback also accepts null to signal the user cleared the input. |
| htmlName | string | HTML name attribute for form submissions. | |
| autoComplete | string | HTML autocomplete attribute. | |
| width | SizeValue | Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned. | |
| hasAutoFocus | boolean | Whether to focus the input on mount. | |
| onFocus | (e: FocusEvent<HTMLInputElement>) => void | Callback fired when the input receives focus. | |
| onBlur | (e: FocusEvent<HTMLInputElement>) => void | Callback fired when the input loses focus. | |
| onEnter | () => void | Callback fired when the user presses the Enter key. | |
| containerXstyle | StyleXStyles | StyleX styles for the field container (label, control, and status together). No effect inside InputGroup (style the InputGroup instead) or in a horizontal-labels FormLayout. Root xstyle, className, and style are not accepted. | |
| containerClassName | string | Class name for the field container. Same targeting as containerXstyle. | |
| containerStyle | CSSProperties | Inline styles for the field container. Same targeting as containerXstyle. | |
| controlXstyle | StyleXStyles | StyleX styles for the visual control box: the element that paints the border and background and holds icons and end controls. Never the native input. | |
| controlClassName | string | Class name for the visual control box. | |
| controlStyle | CSSProperties | Inline styles for the visual control box. |
Usage guidance
- Let people paste formatted numbers: a pasted 1,234,234,234 is read under the field's locale and commits as 1234234234 on blur. Typing is never intercepted.
- Set min, max, and step to guide users toward valid values.
- Show units (e.g. "%" or "GB") so users know what the number represents.
- Set isWheelEnabled={false} when the input appears in a scrolling surface where wheel gestures should always scroll the page.
- Avoid: Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.
- Avoid: Set both isOptional and isRequired on the same field.
- Avoid: Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.
Theming API
{
"targets": [
{
"className": "astryx-number-input",
"visualProps": [
"size",
"status"
],
"states": [
"disabled",
"readonly"
]
}
],
"derived": [
{
"property": "padding",
"expand": "container"
},
{
"property": "borderRadius",
"vars": [
"--_field-radius"
]
}
]
}
Anatomy
- Label: The label for the number input.
- Description: Additional description text below the label.
- Icon: An optional icon within the input.
- Placeholder: Placeholder text shown when the input is empty.
- Number steppers: Optional buttons that increment or decrement by the configured step.
Examples
- Number Input: A number input for quantity entry.
- NumberInput — Clearable: Number input with a clear button, unit suffix, and min/max constraint
- NumberInput — Range Constrained: Number input with min/max boundaries and a helper description
- NumberInput — Status Variants: Number inputs showing error, warning, and success validation states
- NumberInput — With Units: Number input with a percentage unit suffix and valid range