Description:
React Number Flow Input is a zero-dependency React number input component that creates editable animated numbers with digit rolls, formatted separators, and native form support.
It works well for price fields, quantity inputs, dashboard controls, financial forms, and any React number input example where the value should feel more polished than a plain <input type="number">.
Features
- Animates digits as users type.
- Rolls changed digits with a barrel wheel effect.
- Formats large numbers with locale separators.
- Accepts controlled and uncontrolled values.
- Handles decimal limits and negative values.
- Preserves raw numeric text for precision-sensitive fields.
- Mirrors values into native form submissions.
- Injects scoped styles on first mount.
Use Cases
- Pricing forms get animated currency-style numeric fields.
- Dashboard controls display live numeric edits with clear feedback.
- Checkout forms collect quantities, totals, discounts, and limits.
- Finance apps keep exact raw values for large numbers.
How To Use It
Install React Number Flow Input
Install the package with package managers
npm install @daformat/react-number-flow-input
yarn add @daformat/react-number-flow-input
pnpm add @daformat/react-number-flow-input
bun add @daformat/react-number-flow-input
deno add npm:@daformat/react-number-flow-inputUse the component inside a client component when you work in the Next.js App Router. The input handles user events and DOM animation, so the file should include "use client".
Quick Start Usage
This minimal setup renders an animated numeric field with grouped thousands and a change callback.
"use client";
import { NumberFlowInput } from "@daformat/react-number-flow-input";
export default function RevenueInput() {
return (
<NumberFlowInput
defaultValue={8750}
format
// Receives the parsed number after each valid edit.
onChange={(value) => console.log("Revenue value:", value)}
/>
);
}Example 1: Controlled Price Input
Use a controlled value when the number belongs to React state, a form store, or a server action draft.
"use client";
import { useState } from "react";
import { NumberFlowInput } from "@daformat/react-number-flow-input";
export function ProductPriceField() {
const [price, setPrice] = useState<number | undefined>(149.99);
return (
<label className="grid gap-2">
<span className="text-sm font-medium">Product price</span>
<NumberFlowInput
value={price}
// Updates React state with the parsed numeric value.
onChange={setPrice}
// Adds grouped separators to the visible number.
format
// Keeps the decimal part to two digits during editing.
decimalScale={2}
// Adds a custom wrapper class for your app stylesheet.
className="rounded-md border px-3 py-2 text-lg"
placeholder="0.00"
/>
</label>
);
}Example 2: Percentage Input with Validation
Use isAllowed when the user can type only a specific numeric range.
"use client";
import { NumberFlowInput } from "@daformat/react-number-flow-input";
export function DiscountInput() {
return (
<NumberFlowInput
defaultValue={15}
decimalScale={1}
maxLength={5}
placeholder="Discount percent"
// Rejects edits outside the accepted range.
isAllowed={(value) => value == null || (value >= 0 && value <= 100)}
// Runs only after a valid edit passes validation.
onChange={(value) => console.log("Accepted discount:", value)}
/>
);
}Example 3: Native Form Submission
Use name, required, min, and max when the field should participate in a normal HTML form submission.
export function InventoryForm() {
return (
<form action="/products/update-stock" method="post" className="grid gap-4">
<label className="grid gap-2">
<span>Stock quantity</span>
<NumberFlowInput
name="stock"
defaultValue={24}
required
min={0}
max={5000}
format
// The visible editable value stays animated.
className="rounded border px-3 py-2"
/>
</label>
<button type="submit">Save stock</button>
</form>
);
}Example 4: High-Precision Numeric Text
Use onChangeText when JavaScript number precision can damage the value. This matters for large IDs, token amounts, exact currency strings, and big-decimal workflows.
"use client";
import { useState } from "react";
import { NumberFlowInput } from "@daformat/react-number-flow-input";
export function LedgerAmountField() {
const [rawAmount, setRawAmount] = useState("1000000000000000000.00");
return (
<div className="grid gap-2">
<NumberFlowInput
value={rawAmount}
format
decimalScale={2}
// Keeps the exact raw string from the input.
onChangeText={setRawAmount}
// The parsed number remains available for normal numeric workflows.
onChange={(value) => console.log("Parsed value:", value)}
/>
<code>Stored raw value: {rawAmount}</code>
</div>
);
}Example 5: Locale-Aware Number Formatting
Set locale when your UI needs region-specific group and decimal characters.
"use client";
import { NumberFlowInput } from "@daformat/react-number-flow-input";
export function LocalizedBudgetInput() {
return (
<NumberFlowInput
defaultValue={1234567.89}
format
locale="de-DE"
decimalScale={2}
// Accepts both "." and the locale decimal character during input.
onChangeText={(raw) => console.log("Raw internal value:", raw)}
/>
);
}Configuration Options
value(number | string | undefined): Sets the controlled value. External changes animate by default after the first mount.defaultValue(number | string): Sets the uncontrolled starting value.onChange((value: number | undefined) => void): Runs with the parsed number. Intermediate text states returnundefined.onChangeText((rawText: string) => void): Runs with the raw numeric string. Use it for exact precision storage.animateOnValueChange(boolean): Controls animation for externalvalueprop updates. Defaults totrue.format(boolean | (raw: string) => string): Adds grouped formatting throughIntl.NumberFormator a custom formatter.locale(string | Intl.Locale): Sets decimal and group separators for formatted display.decimalScale(number): Limits fractional digits. A value of0blocks decimal entry.autoAddLeadingZero(boolean): Converts.5to0.5and-.5to-0.5.allowNegative(boolean): Accepts a leading negative sign.maxLength(number): Limits the raw input length before formatting.minLength(number): Passes the minimum length constraint to the hidden native input.min(number): Passes the minimum numeric constraint to the hidden native input.max(number): Passes the maximum numeric constraint to the hidden native input.isAllowed((value: number | null) => boolean): Validates each proposed edit before the component commits it.id(string): Sets the component ID.name(string): Sets the hidden native input name for form submission.form(string): Associates the hidden native input with a form ID.required(boolean): Marks the hidden native input as required.placeholder(string): Sets placeholder text on the editable display.className(string): Applies a class to the root wrapper.style(React.CSSProperties): Applies inline styles to the root wrapper.onFocus(() => void): Runs when the editable display receives focus.onBlur(() => void): Runs when the editable display loses focus.autoFocus(boolean): Focuses the editable display on mount.
API Methods
// React Number Flow Input exposes no plugin-style public methods.
// Use React props for updates and a forwarded ref for direct DOM access.
import { useRef } from "react";
import { NumberFlowInput } from "@daformat/react-number-flow-input";
export function FocusableAmountInput() {
const amountRef = useRef<HTMLElement>(null);
function focusAmountField() {
// Focuses the contenteditable element.
amountRef.current?.focus();
}
return (
<>
<NumberFlowInput ref={amountRef} defaultValue={3200} format />
<button type="button" onClick={focusAmountField}>
Focus amount
</button>
</>
);
}Events and Callbacks
<NumberFlowInput
defaultValue={2500}
format
// Runs after the component parses a valid numeric value.
onChange={(value) => {
console.log("Parsed number:", value);
}}
// Runs with the exact raw numeric text.
onChangeText={(rawText) => {
console.log("Raw text:", rawText);
}}
// Runs when the editable display receives focus.
onFocus={() => {
console.log("Amount field focused");
}}
// Runs when the editable display loses focus.
onBlur={() => {
console.log("Amount field blurred");
}}
/>Alternatives and Related Resources
FAQs
Q: Does React Number Flow Input support Next.js App Router?
A: Yes. Use it inside a client component. The component handles DOM animation and input events after hydration.
Q: Why does onChange round very large numbers?
A: onChange returns a JavaScript number. Use onChangeText and string values when you need exact digits beyond Number.MAX_SAFE_INTEGER.
Q: Can I use it for currency inputs?
A: Yes. Use format, decimalScale={2}, and a custom formatter for a currency prefix. Store exact currency values with onChangeText when precision matters.
Q: Does it need a CSS import?
A: No. The component injects minimal scoped styles on first mount. You can customize the visible editable element through its data attributes.




