Open-source project
dm4t2/vue-currency-input avatar
dm4t2/vue-currency-input

vue-currency-input masks any Vue input with Intl formatting

Easy input of currency formatted numbers for Vue.js.

693 stars72 forksTypeScriptMIT

At a glance

What is it?
vue-currency-input is a Vue Composition API component that adds locale aware currency formatting to an input field, hiding the mask on focus and validating the value range, and it works with Vue 2.7 and Vue 3 alike. It suits teams shipping money entry forms in several locales, and it leaves the currency and locale decisions to you.
Who is it for?
Adopt vue-currency-input when a form needs a money field that behaves correctly across locales and you want the same behaviour on Vue 2.7 and Vue 3, and write a plain number input with your own formatting when the field has to be a raw numeric value for a downstream API.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 152 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It decorates an input you already have rather than replacing it

The framing of this component is unusual, and it is the reason the library works across design systems. The README says the component is built on top of the Vue Composition API and that it enables you to decorate any input component with currency format capabilities. The feature list names the frameworks it is meant to sit inside, including Vuetify, Quasar and Element Plus, each of which has its own text field component with its own validation rules, its own slots and its own visual language. A library that shipped its own input would have to reimplement all of that. This one takes the input you already use and layers currency behaviour on it, so the visual result still looks like the rest of your application. The consequence for an adopter is that the input must be compatible with the decoration, which in practice means it has to accept a value and emit input events, and that a good part of the work is figuring out how your framework's field wants to be wired.

Formatting comes from Intl.NumberFormat, not from a hand written mask

The component is described as allowing an easy input of currency formatted numbers based on the ECMAScript Internationalization API, Intl.NumberFormat, and the feature list puts it as built on standards, ensuring the right locale dependent formatting by using Intl.NumberFormat. That choice has consequences worth understanding. The browser's own locale data decides where the group separators go, which symbol appears, and how the decimal separator is drawn, so the behaviour is consistent with the rest of the platform and with every other tool that uses the same API. A hand written mask would have to encode every locale's conventions itself, and would eventually disagree with the platform about some of them. The project topics list the input mask and ECMA-402 keywords, which is an accurate summary of the mechanism. The trade is that the component is not free to invent formatting the platform does not define, so unusual requirements, a currency that renders in a specific non standard way for instance, will not be something it can accommodate.

The mask is hidden on focus so the raw number is editable

The third listed feature is unobtrusive input by hiding the formatting on focus. This is the detail that makes the component feel right in use, and it is a deliberate deviation from what a currency mask usually does. A permanently masked field, with the symbol and the group separators painted in while the user types, is unpleasant to edit because the caret has to be threaded through the decoration. By dropping to a plain number while the field has focus, the user gets an ordinary text input to type into, and the formatted presentation returns when focus leaves. The consequence for a form designer is that the field looks different in its resting state and its editing state, and any validation message or helper text that sits under the field has to account for the change in the field's rendered width. It is also worth noting that the formatted and unformatted values are different strings, so any test that asserts on the input's text has to know which state it is in.

Range validation is built in, and the boundaries are yours to set

The last feature in the list is built-in value range validation. A money field without limits is a common source of bad data, because the browser's number input only stops a value from being non-numeric, not from being a negative amount or a hundred billion. Having the check inside the component means the form does not need a separate rule to express the same bound twice, once for the UI and once for the model. The documentation does not spell out the API in the README itself, so the exact prop names and the error surface are things to look up in the guide rather than guess. What the README makes clear is the intent, that the component enforces a range, and that this is part of the package rather than something a user has to build on top.

One package, two Vue majors, and a peer dependency to match

The package metadata is worth reading before you install, because it answers the compatibility question directly. The peer dependency is declared as vue at ^2.7 || ^3.0.0, so Vue 2.7 and Vue 3 are both supported by the same package, and the feature list states that it supports both Vue 2 and Vue 3. The peer dependency block in package.json is where the compatibility promise is written down:

json
  "peerDependencies": {
    "vue": "^2.7 || ^3.0.0"
  },

The published version is 3.2.2, and the distribution shape is modern: an ES module entry at ./dist/index.mjs, a CommonJS entry at ./dist/index.cjs, types at ./dist/index.d.ts, and an exports map that routes import, require and types separately, with sideeffects set to false so a bundler can drop it when nothing imports it. The peer dependency rather than a direct dependency is the important part for an application team, since the component uses the Vue version the host application provides, and a mismatch between the installed Vue and what the component was compiled against shows up as confusing behaviour rather than a clean error.

The build and test tooling is a small modern TypeScript stack

The build is a two stage process and the package.json scripts spell it out. The prebuild step clears dist and temp with rimraf, and the build script runs tsc with emitDeclarationOnly and a declaration outDir of temp/types, then rollup with the repository's rollup.config.js. So type declarations are produced by the compiler and the JavaScript bundle is produced by rollup, with the temp directory acting as the handoff. The test runner is vitest, with a coverage variant and a vitest.config.ts at the top level, and the lint script runs eslint with no fix and a zero warning budget over JavaScript and TypeScript files, using the @vue/eslint-config-typescript and @vue/eslint-config-prettier sets with prettier for formatting. There is also a pnpm-lock.yaml, so the dependency versions are pinned by a lockfile rather than a range. The docs are a VitePress site, with dev and build scripts pointing at the docs directory, and the published documentation site is where the guide and the playground live.

Where vue-currency-input is the wrong tool

Three situations argue against it. The first is a form that must submit a raw number. Some backends and some downstream libraries want a numeric field or a string of digits, and a currency component that formats for display makes you parse the value back out on the way out. The second is a non-browser runtime. The formatting depends on Intl.NumberFormat, which is a platform API supplied by the browser, so server-side rendering has to hydrate or reproduce that behaviour, and a Node or worker environment without full ICU data may format differently from the browser, which is the classic source of a hydration mismatch in a currency field. The third is a design system that has already solved this. A large component library with its own locale, currency and rounding policy usually implements its own masking component, and bolting a second one onto it produces two sources of truth about the same field. A fair alternative for smaller projects is a plain number input with the Intl.NumberFormat call made in the validation layer, which is less capable but far less machinery for a form with a single money field. The project also has a maintenance caveat. The last push was on 2026-05-25 and the most recent release, 3.2.2, is from 2025-12-17, so the code base is not archived but the release stream has been quiet for a while, which is worth knowing before you build a product feature on it.

Editorial conclusion

Adopt vue-currency-input when a form needs a money field that behaves correctly across locales and you want the same behaviour on Vue 2.7 and Vue 3, and write a plain number input with your own formatting when the field has to be a raw numeric value for a downstream API. Verify first which Vue version your project runs and whether the input you want to decorate accepts a value prop and an input event, since the component decorates an existing input rather than replacing it, then read the guide for the currency and locale options before assuming the default is right for your market.

Frequently asked questions

Does vue-currency-input work with Vue 2 and Vue 3?

Yes. The feature list states that it supports both Vue 2 and Vue 3, and the package declares vue as a peer dependency of ^2.7 || ^3.0.0, so the same package works on Vue 2.7 and Vue 3.

How does vue-currency-input format numbers?

It uses the ECMAScript Internationalization API, Intl.NumberFormat, so the locale dependent formatting comes from the platform's own locale data rather than from a hand written mask. The project topics list ECMA-402 and input mask for the same reason.

Can I use vue-currency-input with Vuetify, Quasar or Element Plus?

Yes. The component is built on the Vue Composition API and decorates any input component, and the feature list names Vuetify, Quasar and Element Plus as examples. The input you pass still has to accept a value and emit input events for the decoration to work.

What does the mask do while the field has focus?

The formatting is hidden on focus, so the field behaves like an ordinary number input while it is being edited, and the currency formatting returns when focus leaves. That is the documented unobtrusive input behaviour.

Is vue-currency-input actively maintained?

The repository is not archived and the last push was on 2026-05-25. The most recent release is 3.2.2 from 2025-12-17, with earlier versions 3.2.1 and 3.2.0 both from January 2025, so the release stream has been quiet for a while.

Official sources

  1. dm4t2/vue-currency-input on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dm4t2-vue-currency-input.svg)](https://hysenlabs.com/projects/dm4t2-vue-currency-input)

Was this useful?