react-intl-number-input
v0.8.6
Published
A React component for masked and formatted number input.
Maintainers
Readme
react-intl-number-input
A React component for masked and formatted number input with Intl.NumberFormat locale support.
Requirements
- React:
>=16.8.0 <20
Install
npm install react-intl-number-inputUsage
import React, { useState } from 'react';
import IntlNumberInput from 'react-intl-number-input';
function App() {
const [value, setValue] = useState(0);
const [maskedValue, setMaskedValue] = useState('0.00');
const handleChange = (event, nextValue, nextMaskedValue) => {
setValue(nextValue);
setMaskedValue(nextMaskedValue);
};
return (
<div>
<IntlNumberInput onChange={handleChange} />
<p>value: {value}</p>
<p>maskedValue: {maskedValue}</p>
</div>
);
}TypeScript
import React, { useState } from 'react';
import IntlNumberInput, {
IntlNumberInputProps,
} from 'react-intl-number-input';
function App() {
const [value, setValue] = useState<number>(0);
const handleChange: IntlNumberInputProps['onChange'] = (event, val, masked) => {
setValue(val);
console.log('Numeric:', val, 'Masked:', masked);
};
return <IntlNumberInput value={value} onChange={handleChange} locale="pt-BR" />;
}React 18+:
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);React 16/17:
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));Properties
| Name | Type | Default | Description |
| --- | --- | :---: | --- |
| value | number | string | 0 | Controlled numeric value |
| locale | string | 'en-US' | BCP 47 language tag (Intl locales) |
| prefix | string | '' | Prefix shown in the masked value |
| suffix | string | '' | Suffix shown in the masked value |
| precision | number | 2 | Fraction digits (clamped to 0–20) |
| onChange | function | — | (event, value, maskedValue) => void |
| onBlur | function | — | (event, value, maskedValue) => void |
| disabled | boolean | false | Disables the input |
| autoFocus | boolean | — | Native input autofocus |
| minValue | number | — | Minimum allowed value |
| maxValue | number | — | Maximum allowed value |
| showStepButtons | boolean | false | Renders built-in +/- step buttons (ignored when renderControls is set) |
| renderControls | function | — | (props: ControlsRenderProps) => ReactNode — custom stepper UI |
| step | number | 1 | Step increment in display units (e.g. 1 with precision={2} adds 0.01) |
| inputMode | 'numeric' | 'decimal' | auto | Overrides mobile keyboard mode |
| className | string | — | Input CSS class |
| style | object | — | Input inline styles |
| id | string | — | Input id |
| name | string | — | Input name |
| placeholder | string | — | Input placeholder |
| readOnly | boolean | — | Read-only input |
| required | boolean | — | Required input |
| tabIndex | number | — | Input tab index |
The component also accepts standard <input> attributes such as ref, onFocus, onKeyDown, aria-*, data-*, and autoComplete. Custom onChange and onBlur callbacks receive the clamped numeric value and the formatted masked string.
Accessibility (a11y)
The component is built with accessibility in mind:
- Uses
role="spinbutton"to identify as a numeric input. - Provides
aria-valuenow,aria-valuemin, andaria-valuemaxattributes. - Support for
forwardRefallows linking labels and managing focus programmatically. - Built-in step buttons have descriptive
aria-labelattributes.
Examples
Basic Usage
// maskedValue: 1,234,567.89
<IntlNumberInput />// maskedValue: 12,345.6789
<IntlNumberInput precision={4} />// maskedValue: $1,234,567.89
<IntlNumberInput prefix="$" />// maskedValue: 1,234%
<IntlNumberInput suffix="%" precision={0} />// maskedValue: 12.50% — type digits only; decimal is implied by precision
<IntlNumberInput suffix="%" precision={2} />// maskedValue: R$ 1.234.567,89
<IntlNumberInput locale="pt-BR" prefix="R$ " precision={2} />// With min/max and step buttons
<IntlNumberInput
value={50}
minValue={0}
maxValue={100}
step={5}
precision={0}
showStepButtons
onChange={(event, value) => console.log(value)}
/>// Custom controls (layout is up to you — wrap in a parent div as needed)
<div className="amount-field">
<IntlNumberInput
value={12.34}
precision={2}
step={1}
minValue={0}
maxValue={100}
onChange={(event, value) => console.log(value)}
renderControls={({ increment, decrement, setValue, value, formattedValue, min, max, disabled }) => (
<div className="amount-controls">
<button type="button" onClick={() => decrement()} disabled={disabled}>-</button>
<span>{formattedValue}</span>
<button type="button" onClick={() => increment()} disabled={disabled}>+</button>
<button type="button" onClick={() => setValue(max ?? value)} disabled={disabled}>
Max
</button>
</div>
)}
/>
</div>Programmatic Focus (ref)
import React, { useRef } from 'react';
import IntlNumberInput from 'react-intl-number-input';
function FocusExample() {
const inputRef = useRef(null);
return (
<>
<IntlNumberInput ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>
Focus Input
</button>
</>
);
}renderControls replaces showStepButtons when both are provided. The component renders the <input> and your controls as siblings (no wrapper), so you control layout in the parent.
How input works
Users type digits; the component applies locale formatting and optional prefix/suffix. For precision={2}, typing 1234 becomes 12.34. Negative values are supported when - is present in the input.
Contributing
See CONTRIBUTING.md for development setup, testing, and publishing instructions.
Local development requires Node.js 20+. The published package has no Node.js version constraint for consumers.
Changelog
See CHANGELOG.md for release history.
