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 | undefined—Controlled value
defaultValuenumber—Uncontrolled initial value
onChange(v: number) => void—Called on change
minnumber—Minimum value
maxnumber—Maximum value
stepnumber1Increment step (Arrow keys)
bigStep / largeStepnumberstep × 10Step for Shift+Arrow / PageUp / PageDown (aliases)
precisionnumber—Decimal places for display and clamping
format"decimal" | "currency" | "percent""decimal"Display format via Intl.NumberFormat
currencystring—ISO currency code (required when format="currency")
localestring"en-US"Intl locale for formatting
labelstring—Visible label. Also the scrub drag handle when scrubable is set
hintstring—Helper text below input
errorstring—Error 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
repeatRepeatOptions—Hold-to-repeat configuration: { initialDelay?, interval?, accel? }
prefixstring—Static text before the value (e.g. "RM")
suffixstring—Static text after the value (e.g. "kg")
placeholderstring—Input placeholder
onBlurFocusEventHandler—Blur callback
onFocusFocusEventHandler—Focus callback
namestring—Native form name attribute
idstring—Override the generated input id
autoFocusboolean—Auto-focus on mount
classNamestring—Extra class on the root element
styleCSSProperties—Inline 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