Use when implementing enter and format monetary values.
Enter and format monetary values
A Currency Input is a specialized numeric form field for entering monetary values. It combines the raw numeric input behavior of a number field with locale-aware formatting, currency symbol display, and financial validation constraints.
Unlike a generic number input, a currency input formats the value as the user types (e.g., displaying $1,299.99 instead of 1299.99), handles decimal precision based on the currency (USD uses 2 decimal places, JPY uses 0), and positions the currency symbol or code according to locale conventions.
references/pattern.md, then choose the smallest viable variation.| Key | Action |
|---|---|
Tab | Moves focus to the currency input |
Shift + Tab | Moves focus to the previous element |
0–9 | Enters numeric digits |
. or , | Enters decimal separator (locale-dependent) |
Backspace | Removes the last character |
Delete | Removes the character after the cursor |
Arrow Left/Right | Moves cursor within the input |
type="number" for CurrencyThe Problem:
<input type="number"> shows browser spinner arrows, rejects thousands separators (commas), and handles decimals inconsistently across browsers and locales.
<!-- Bad -->
<input type="number" step="0.01" min="0" />
How to Fix It? Use type="text" with inputmode="decimal".
<!-- Good -->
<input type="text" inputmode="decimal" pattern="[0-9]*[.,]?[0-9]{0,2}" />
The Problem:
Reformatting the value on input events moves the cursor unexpectedly, especially when adding thousands separators mid-entry.
How to Fix It? Format only on blur; show raw decimal input during typing.
input.addEventListener('blur', () => {
const raw = parseFloat(input.value.replace(/[^0-9.-]/g, ''));
if (!isNaN(raw)) {
input.value = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(raw);
}
});
input.addEventListener('focus', () => {
// Strip formatting for editing
input.value = input.value.replace(/[^0-9.]/g, '');
});
The Problem:
Sending "$1,299.99" to a backend that expects 1299.99 (or 129999 as integer cents) causes parse errors or incorrect amounts.
How to Fix It? Use a hidden input for the raw value, or strip formatting before submission.
<input type="text" id="amount-display" value="$1,299.99" />
<input type="hidden" id="amount-raw" name="amount" value="1299.99" />
form.addEventListener('submit', () => {
const raw = parseFloat(displayInput.value.replace(/[^0-9.]/g, ''));
hiddenInput.value = raw.toFixed(2);
});
For full implementation detail, examples, and testing notes, see references/pattern.md.
Pattern page: https://uxpatterns.dev/patterns/forms/currency-input