IMask Currency Input with Live Formatting — Free JS Snippet

IMask Currency Input with Live Formatting · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

What's included

Features

Live thousands separators without caret jumping
Per-currency symbol, decimals, grouping and radix from one config table
mapToRadix accepts a typed dot or comma as the decimal mark
typedValue returns a real Number, no string cleaning needed
Integer minor units computed with Math.round for safe money handling
Amount preserved when switching currency via updateOptions()
Maximum amount clamp with visible feedback
Numeric mobile keyboard via inputmode="decimal"

About this UI Snippet

IMask Currency Input with Live Formatting — HTML, CSS & JavaScript

Screenshot of the IMask Currency Input with Live Formatting snippet rendered live

Formatting money while someone types is harder than formatting it afterwards. A naive onInput handler that inserts commas fights the caret — the cursor jumps to the end, deleting a comma does nothing, and pasting "1,234.5" breaks. IMask solves the caret problem properly: it tracks where the cursor should sit after each edit, so the number reformats live without disturbing what the user is doing. This snippet wraps that in a complete currency field.

The important design decision is separating what the user sees from what your code uses. The input shows "1,234,567.5". mask.typedValue is the same amount as a genuine JavaScript Number, with none of the formatting to strip. And the value you should actually send to a server or payment API is neither of those: it is integer minor units — 123456750 cents — computed with Math.round(n * 10^scale). Floating-point numbers cannot represent most decimal fractions exactly, so storing 0.1 + 0.2 style values is how money bugs happen. All three are shown side by side so the difference is unmissable.

Currency rules live in one table: symbol, decimal places, thousands separator and radix character. Euro amounts group with dots and use a comma as the decimal mark, so switching currency calls mask.updateOptions() with the new settings and reassigns typedValue so the same amount re-renders correctly. Yen has zero decimal places and rounds accordingly. mapToRadix lets a German-style field accept a typed dot as a decimal comma, which is what people with US keyboards will actually press.

Two small choices affect feel. padFractionalZeros is off, so typing "12" does not become "12.00" mid-entry and steal the caret; format on blur if you want the trailing zeros. And the max option clamps entry at a ceiling with a visible message when reached, rather than silently ignoring keystrokes.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant like Claude to format the field with Intl.NumberFormat on blur, add lakh/crore grouping for INR with a custom mask, or make the maximum configurable per currency.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a currency input with IMask 7 loaded from a CDN.

Requirements:
- Use a Number mask with scale, signed: false, thousandsSeparator, radix, mapToRadix, min and max, and padFractionalZeros: false.
- Provide a currency switcher (USD, EUR, GBP, JPY) driven by one config table; switching calls mask.updateOptions() and re-assigns typedValue so the amount carries over.
- Display three values: the formatted text, mask.typedValue as a Number, and integer minor units from Math.round(n * 10^scale).
- Show a visible message when the maximum is reached.
- Use inputmode="decimal" so mobile keyboards show a numeric pad.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="cu-card">
  <div class="cu-top">
    <label class="cu-label" for="cuAmount">Amount</label>
    <div class="cu-cur" role="group" aria-label="Currency">
      <button type="button" data-c="USD" class="on">USD</button>
      <button type="button" data-c="EUR">EUR</button>
      <button type="button" data-c="GBP">GBP</button>
      <button type="button" data-c="JPY">JPY</button>
    </div>
  </div>
  <div class="cu-field">
    <span class="cu-sym" id="cuSym">$</span>
    <input id="cuAmount" type="text" inputmode="decimal" autocomplete="off" placeholder="0.00" value="1234567.5">
  </div>
  <p class="cu-hint" id="cuHint"></p>
  <dl class="cu-read">
    <div><dt>Displayed</dt><dd id="cuShown">-</dd></div>
    <div><dt>Number</dt><dd id="cuNum">-</dd></div>
    <div><dt>Minor units (send this)</dt><dd id="cuMinor">-</dd></div>
  </dl>
</div>

Step by step

How to Use

  1. 1
    Type an amountType digits into the field. Thousands separators appear as you go, and the caret stays where you expect.
  2. 2
    Read the three valuesCompare the displayed text, the parsed number and the integer minor units underneath.
  3. 3
    Switch currencyClick EUR. Grouping flips to dots, the decimal mark becomes a comma, and the amount carries across.
  4. 4
    Try JPYYen has no decimals, so the fractional part is rounded away and minor units equal the amount.
  5. 5
    Paste a valuePaste "9,999.99" and watch the field normalise it under the active currency rules.

Real-world uses

Common Use Cases

Checkout and donation forms
Let users enter an amount with confidence. Pair with the credit card input for a complete payment form.
Invoicing and budgeting tools
Enter line items and budgets in multiple currencies with correct local formatting.
ADMIN
Pricing admin screens
Edit product prices without a stray comma ever reaching the database.
Learning money handling
Demonstrates why money should travel as integer minor units rather than floats.

Got questions?

Frequently Asked Questions

Floating-point numbers cannot exactly represent many decimals. Integer cents (or the smallest unit) add and compare exactly, which is why payment APIs use them.

For a Number mask it returns a real JavaScript number, so you do not have to strip separators from the displayed string.

IMask recomputes the cursor position after every formatting change, unlike a naive input handler that resets it to the end.

Padding zeros while typing rewrites the text under the caret. Apply zero padding on blur if you want it.

Add an entry to the CURRENCIES table with its symbol, scale, thousands separator and radix character.

Yes, with a pattern mask and blocks, but keeping the symbol outside makes the value trivial to read and avoids editing the prefix by accident.

Yes. Use the JSX, Vue, Angular or Tailwind export buttons on this page to convert the markup and styles. The behaviour comes from IMask, so in a framework project install it with npm install imask (or react-imask / vue-imask / angular-imask) instead of the CDN tag, create it in useEffect / onMounted / ngAfterViewInit, and release it with destroy() when the component unmounts.