Money inputs show one number and send another
An amount field should look friendly to a person and stay exact for the API. Notes on grouping, decimals per currency and limits.
Amount fields appear everywhere in payments: payment links, invoices, refunds, FX calculators. They look trivial and rarely are. The core idea that made them easier for me: the field shows one number and sends another. The person sees 12,34,567.50 with separators in their locale. The API receives 1234567.50, a plain decimal string with no formatting at all.
Paste “1234567.891” or type past the limits.
Decimals belong to the currency
Most currencies have two decimal places, but not all. Japanese yen has none and Kuwaiti dinar has three. So the number of decimals the field allows comes from the selected currency, not from a global setting. Switch the currency above and the limit changes.
Grouping belongs to the locale
Indian rupees group in lakhs and crores (12,34,567); US dollars group in thousands (1,234,567). Number.prototype.toLocaleString handles this if you pass the right locale, so there’s no need to write grouping logic by hand.
Limits belong to the backend contract
On one product, the backend accepted at most 11 whole digits and 4 decimal places. Rather than letting people type a longer number and then showing an error, the field trims to the limit as they type and says why. Validation that matches the contract exactly saves a round trip and a confusing error from the server.
The caret problem again
Every time you add or remove a separator, the caret wants to jump. The fix is the same as for card numbers: count the meaningful characters (digits and the decimal point) to the left of the caret, reformat, then put the caret back after the same count.
Never do maths on the formatted string
Parse once into a clean value, calculate with that, and format only for display. When totals matter, work in the smallest unit (cents, paise) or round deliberately at each step, so that 0.1 + 0.2 never shows up on an invoice.