Shipmind Labs

A money input should not hold a float, and it should not assume two decimals.

Both assumptions look harmless right up until they reach production.

The float is the obvious one. 0.1 + 0.2 in binary floating point is not 0.3, and once a value has passed through a JavaScript number you cannot reliably get the original back. Rounding at the end does not fix that. What fixes it is that the amount never becomes a float at all: it is an integer in minor units from the moment you parse it, it stays an integer through validation and transport, and it becomes a human-readable string only at the last step, for display. Formatting is an output concern. Arithmetic does not touch the formatted form.

The two-decimals assumption is the quieter one. ISO 4217 assigns each currency an exponent, and it is not always 2. JPY has 0 minor units. Several currencies use 3. So your field cannot hardcode a scale, it needs the exponent per currency, and it needs an explicit override path for the cases the table does not cover: internal accounting precision, crypto amounts that need more digits than any fiat table will ever list, a pricing unit your finance team defined for one specific product. An override the caller sets deliberately is safe. A silent fallback to 2 is not.

Then comes the part most teams underestimate: partial input. A user typing an amount produces a broken string on every keystroke, and the separators differ by locale. "1,234.56", "1.234,56" and "1 234,56" are the same amount written by three different people. Guessing which one is the decimal separator with a regex works until someone from another market opens your form. Intl already exposes the locale's group and decimal separators, so your parser can read them from the locale instead of inventing its own rules, and it has to stay stable mid-typing, when the input is still "1." or "1,2" and not yet a valid number.

We pulled this into a small open-source component we maintain, amountfield, because we kept rebuilding the same logic across payment forms, invoicing screens and admin panels. Same three problems every time.

The general shape holds well beyond the UI: integers in minor units, currency exponent as data rather than a constant, formatting strictly at the edge.

If you work across multiple currencies, the two-decimals assumption probably surfaced somewhere already, in the input, in the totals, or in a downstream report.

Was this useful?

Building something similar?

or email hello@shipmindlabs.com