Components

NumberInput

Headless numeric text input with spinbutton semantics.

When to Use

Use NumberInput when a value is a number and people should be able to type it or step it up and down with the keyboard, such as quantity or percentage. Use Input when the text only looks numeric, such as a postal code, account number, or phone number, because those values should not be incremented or clamped.

Features

  • Renders an editable text input with role="spinbutton".
  • Can be controlled or uncontrolled.
  • Supports min, max, step, largeStep, and precision formatting.
  • Supports keyboard stepping with arrows, Page Up/Down, Home, and End.
  • Supports custom parser and formatter functions.
  • Renders a hidden input for native form submission when named.

Import

import { NumberInput } from "@flowstack-ui/atom";

Anatomy

<NumberInput.Root />

API Reference

Root

Renders the root container, inner spinbutton input, and optional hidden form input.

PropTypeDefault
valuenumber | null-
defaultValuenumber-
onValueChange(value: number | null) => void-
minnumber-
maxnumber-
stepnumber1
largeStepnumberstep * 10
precisionnumberInferred from step
clampOnBlurbooleantrue
formatter(value: string) => string-
parser(displayValue: string) => string-
disabledbooleanfalse
readOnlybooleanfalse
requiredbooleanfalse
invalidbooleanfalse
placeholderstring-
namestring-
formstring-
idstring-
ariaLabelstring-
ariaValueText(value: number) => string-
ariaDescribedBystring-
classNamestring-
inputClassNamestring-
childrenReactNode | (state: NumberInputRenderState) => ReactNode-
ARIA attributeValues
aria-labelValue from ariaLabel
aria-valuenowCurrent numeric value when not empty
aria-valueminValue from min
aria-valuemaxValue from max
aria-valuetextResult of ariaValueText
aria-describedbyValue from ariaDescribedBy
aria-invalidPresent when invalid
aria-readonlyPresent when read only
aria-requiredPresent when required
Data attributeValues
[data-slot]"number-input"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid

When children is a function, it receives numericValue, displayValue, isAtMin, isAtMax, disabled, readOnly, handleStep, and inputRef. This supports consumer-owned increment and decrement controls without adding new styled parts to Atom.

Examples

Basic Range

import { NumberInput } from "@flowstack-ui/atom";

export function QuantityInput() {
  return <NumberInput.Root ariaLabel="Quantity" min={0} max={10} step={1} />;
}

Currency Formatting

import { NumberInput } from "@flowstack-ui/atom";

export function CurrencyInput() {
  return (
    <NumberInput.Root
      ariaLabel="Price"
      parser={(value) => value.replace(/[$,]/g, "")}
      formatter={(value) => `$${value}`}
    />
  );
}

The package also exports clampNumberValue, formatNumber, parseNumber, roundToPrecision, and stepNumberValue for consumers that need the same numeric calculations outside the rendered component.

Accessibility

NumberInput follows the WAI-ARIA spinbutton pattern. The inner input renders role="spinbutton".

  • Atom owns aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext, aria-required, aria-readonly, and aria-invalid.
  • Provide an accessible name through ariaLabel or external labeling.
KeyDescription
ArrowUpIncrements by step.
ArrowDownDecrements by step.
PageUpIncrements by largeStep.
PageDownDecrements by largeStep.
HomeMoves to min when provided.
EndMoves to max when provided.

Changelog

See CHANGELOG.md.