react ~6 KB 0 deps v1.2.0 ↗ GitHub ↗

@mshafiqyajid/react-number-input

Headless number input hook and styled component. Decimal, currency, and percent modes via Intl.NumberFormat. Step, min, max, keyboard navigation.

Playground #

Hold +/− to repeat. Shift+arrows or PageUp/PageDown for large step.
Props
TSX
import { NumberInputStyled } from "@mshafiqyajid/react-number-input/styled";
import "@mshafiqyajid/react-number-input/styles.css";

<NumberInputStyled
  value={value}
  onChange={setValue}
/>

Install #

npm install @mshafiqyajid/react-number-input

Quick start #

import { NumberInputStyled } from "@mshafiqyajid/react-number-input/styled";
import "@mshafiqyajid/react-number-input/styles.css";

const [price, setPrice] = useState<number | undefined>(0);
<NumberInputStyled
  value={price}
  onChange={setPrice}
  format="currency"
  currency="MYR"
  min={0}
  label="Price"
  tone="primary"
/>

// Prefix/suffix
<NumberInputStyled value={weight} onChange={setWeight} suffix="kg" />
<NumberInputStyled value={amount} onChange={setAmount} prefix="RM" />

Scrub mode #

Set scrubable to turn the label into a horizontal drag handle — drag right to increase, left to decrease. The cursor changes to an east-west resize cursor.

<NumberInputStyled
  label="Opacity"
  value={opacity}
  onChange={setOpacity}
  min={0}
  max={100}
  scrubable
  scrubPixels={4}
/>

Headless #

import { useNumberInput } from "@mshafiqyajid/react-number-input";

const { inputProps, incrementProps, decrementProps } = useNumberInput({
  defaultValue: 0,
  min: 0,
  max: 100,
  step: 1,
});

return (
  <div className="num">
    <button {...decrementProps}>−</button>
    <input {...inputProps} />
    <button {...incrementProps}>+</button>
  </div>
);

API #

PropTypeDefaultDescription
valuenumber | undefinedControlled value
defaultValuenumberUncontrolled initial value
onChange(v: number) => voidCalled on change
minnumberMinimum value
maxnumberMaximum value
stepnumber1Increment step (Arrow keys)
bigStep / largeStepnumberstep × 10Step for Shift+Arrow / PageUp / PageDown (aliases)
precisionnumberDecimal places for display and clamping
format"decimal" | "currency" | "percent""decimal"Display format via Intl.NumberFormat
currencystringISO currency code (required when format="currency")
localestring"en-US"Intl locale for formatting
labelstringVisible label. Also the scrub drag handle when scrubable is set
hintstringHelper text below input
errorstringError message (overrides hint, sets danger tone)
invalidbooleanfalseForce invalid state without inline error text
requiredbooleanfalseMark field as required
size"sm" | "md" | "lg""md"Input size
tone"neutral" | "primary" | "success" | "danger""neutral"Color tone
disabledbooleanfalseDisable interaction
readOnlybooleanfalseRead-only mode — value shown but not editable
showStepperbooleantrueShow +/− increment buttons
clampOnBlurbooleantrueSnap to min/max on blur
scrubablebooleanfalseEnable drag-to-scrub on the label (drag right = increment, left = decrement)
scrubPixelsnumber4Pixels of horizontal drag per step when scrubable is true
repeatRepeatOptionsHold-to-repeat configuration: { initialDelay?, interval?, accel? }
prefixstringStatic text before the value (e.g. "RM")
suffixstringStatic text after the value (e.g. "kg")
placeholderstringInput placeholder
onBlurFocusEventHandlerBlur callback
onFocusFocusEventHandlerFocus callback
namestringNative form name attribute
idstringOverride the generated input id
autoFocusbooleanAuto-focus on mount
classNamestringExtra class on the root element
styleCSSPropertiesInline style override

Holding an increment/decrement button auto-repeats (configurable via repeat). Home / End jump to min / max. The stepper animation plays a digit-flip effect in the direction of change.

Edit this page on GitHub