These four libraries address different aspects of working with numbers and currency in JavaScript. currency-codes is a pure data utility for looking up ISO 4217 currency metadata (like codes and minor units). currency.js is a lightweight calculator designed to solve floating-point math errors specifically for currency values. dinero.js (specifically the v1 legacy version widely used) is an immutable library for creating, calculating, and formatting money objects with strict precision. numeral is a general-purpose number formatting library that handles large numbers, percentages, and bytes, but lacks built-in currency safety. Choosing the right tool depends on whether you need raw data, safe math, immutable money objects, or just text formatting.
Dealing with money in JavaScript is notoriously tricky. The language's native number type uses floating-point arithmetic, which leads to infamous errors like 0.1 + 0.2 === 0.30000000000000004. For professional developers, picking the right tool isn't just about features — it's about preventing financial bugs. The packages currency-codes, currency.js, dinero.js, and numeral each solve a specific part of this puzzle. Let's break down how they differ and when to use each one.
Before you calculate anything, you often need to know the rules of the currency you are handling. How many decimal places does the Japanese Yen use? (Zero). What is the code for the Euro? (EUR). This is where currency-codes shines. It is purely a lookup table.
currency-codes provides static data based on the ISO 4217 standard. It doesn't do math. It just gives you facts.
import { getCode, getCurrency } from 'currency-codes';
// Get currency code by country
const code = getCode('UNITED STATES');
// Returns: 'USD'
// Get details about a currency
const currency = getCurrency('USD');
// Returns: { code: 'USD', currency: 'US Dollar', number: 840, digits: 2 }
currency.js, dinero.js, and numeral do not provide this metadata. They assume you already know the currency code or the number of decimals. If you use them, you will likely need to install currency-codes alongside them to handle dynamic currency selection.
// With currency.js, you must manually pass the decimal count if it's not default
// It does not look up 'JPY' to know it has 0 decimals automatically
const yen = currency(1000, { symbol: '¥', decimalDigits: 0 });
This is the most critical distinction. Standard JavaScript math breaks money. The libraries here take different approaches to fix it.
currency.js wraps the value and handles the decimal shifting internally. It allows you to chain operations naturally.
import currency from 'currency.js';
const price = currency(19.99);
const tax = currency(1.50);
// Safe addition
const total = price.add(tax);
console.log(total.value); // 21.49 (exact)
console.log(total.format()); // "$21.49"
dinero.js takes a stricter approach. It requires you to work with integers (cents) explicitly to guarantee precision. It creates an immutable object for every operation.
import dinero from 'dinero.js';
// Must pass amount in cents (integer)
const price = dinero({ amount: 1999, currency: 'USD' });
const tax = dinero({ amount: 150, currency: 'USD' });
// Safe addition returns a NEW dinero object
const total = price.add(tax);
console.log(total.getAmount()); // 2149
console.log(total.toFormat()); // "$21.49"
numeral does not solve floating-point errors. It formats numbers that already exist. If you do math with numeral, you are using standard JavaScript math.
import numeral from 'numeral';
// Dangerous: Standard JS math happens first
const badTotal = 0.1 + 0.2;
console.log(numeral(badTotal).format('$0.00')); // "$0.30" (Looks fine, but value is wrong)
// If precision matters, numeral is the wrong tool for calculation
currency-codes has no math capabilities.
Once you have a calculated value, you need to show it to the user. The formatting capabilities vary wildly.
numeral is the most flexible formatter of the group. It handles percentages, bytes, time, and large abbreviations (k, M, B) out of the box.
import numeral from 'numeral';
numeral(1234567).format('0,0'); // "1,234,567"
numeral(0.75).format('0%'); // "75%"
numeral(1024).format('0b'); // "1KB"
numeral(1234.5).format('$0,0.00'); // "$1,234.50"
currency.js focuses strictly on currency formatting. It is simple and handles symbols well, but cannot do percentages or bytes.
import currency from 'currency.js';
const val = currency(1234.5);
val.format(); // "$1,234.50"
val.format({ symbol: '€', pattern: '!#' }); // "€1,234.50"
dinero.js provides robust localization support for currency but requires you to specify the locale explicitly for complex formatting.
import dinero from 'dinero.js';
const d = dinero({ amount: 123450, currency: 'USD' });
d.toFormat('en-US'); // "$1,234.50"
d.toFormat('de-DE'); // "1.234,50 $" (depending on configuration)
currency-codes does not format values.
For complex domains like banking or split payments, simple math isn't enough. You need to ensure data isn't accidentally changed and that splitting money doesn't lose pennies.
dinero.js excels here. Every operation returns a new object (immutability), preventing side effects. It also has a dedicated allocate method to split money fairly without losing cents due to rounding.
import dinero from 'dinero.js';
const amount = dinero({ amount: 100, currency: 'USD' });
// Split $1.00 into 3 parts fairly
const [share1, share2, share3] = amount.allocate([1, 1, 1]);
console.log(share1.getAmount()); // 34
console.log(share2.getAmount()); // 33
console.log(share3.getAmount()); // 33
// Total is still 100. No pennies lost.
currency.js is mutable in its chain (though it returns new instances for operations, it lacks advanced allocation logic). You would have to write your own rounding logic for splits.
import currency from 'currency.js';
// No built-in allocate function
// You must manually calculate shares and handle rounding errors
const total = currency(1.00);
const share = total.multiply(0.333333);
// Rounding issues may arise if not handled carefully
numeral and currency-codes offer no support for allocation or immutability patterns.
Before adopting these tools, check their current status.
dinero.js (v1) is widely used and stable, but the maintainers have been working on a complete rewrite (v2) for several years. The v1 API is considered "legacy" but remains the standard for production apps today. Do not start a new project expecting the v2 API yet, but be aware of the transition.numeral has had periods of low maintenance. While it works perfectly for formatting, ensure it fits your long-term support requirements.currency.js and currency-codes are generally stable and focused, with fewer moving parts to break.| Feature | currency-codes | currency.js | dinero.js (v1) | numeral |
|---|---|---|---|---|
| Primary Goal | ISO Metadata Lookup | Safe Currency Math | Immutable Money Objects | General Number Formatting |
| Math Precision | N/A | ✅ Safe (Internal) | ✅ Safe (Integer based) | ❌ Standard JS Float |
| Immutability | N/A | Partial | ✅ Full | N/A |
| Allocation | ❌ No | ❌ No | ✅ Yes (Built-in) | ❌ No |
| Formatting | ❌ No | ✅ Currency Only | ✅ Currency + Locale | ✅ All Types (%, bytes, etc) |
| Bundle Weight | Tiny | Small | Medium | Small |
Choosing the right library depends entirely on the complexity of your financial logic.
currency-codes is a utility belt item. Almost every serious e-commerce or fintech app should have it installed just to validate codes and find decimal lengths. It pairs well with any of the others.
numeral is your go-to for dashboards. If you are showing "54% growth", "1.2GB used", or "$5M revenue", use numeral. Just don't use it to calculate the revenue.
currency.js is the pragmatic choice for standard e-commerce. If you need to add up a cart, apply a discount, and show the total, it offers the best balance of safety and developer speed. It reads like English and handles the ugly math for you.
dinero.js is the enterprise choice. If you are building a ledger, a banking app, or a system where splitting payments and auditing every cent matters, the strictness of dinero.js is worth the extra verbosity. Its allocation features alone save hours of bug-fixing time.
Final Thought: Never trust native JavaScript numbers with money. Pick a tool that enforces precision, and keep your formatting logic separate from your calculation logic for the cleanest architecture.
Choose currency-codes if you only need to look up static metadata about currencies, such as converting a country code to a currency code or finding the number of decimal places for a specific currency. It does not perform math or formatting, so pair it with a calculation library if you need to handle values. It is the best choice for populating dropdown menus or validating currency codes without adding heavy logic to your bundle.
Choose dinero.js (v1) if you are building a financial application that requires immutable money objects, precise allocation of funds, and strict comparison logic. It is the standard for domains where accuracy is critical, such as banking dashboards or accounting tools. Note that while v1 is stable, the ecosystem is moving toward a new version; ensure your project constraints align with the current v1 API before committing.
Choose currency.js if you need a simple, chainable API to perform addition, subtraction, and formatting on currency values without floating-point errors. It is ideal for shopping carts, simple invoicing, or frontend displays where you need quick math and immediate string output. Avoid it for complex financial ledgers requiring strict immutability or advanced allocation algorithms.
Choose numeral if your primary goal is formatting diverse number types (large integers, percentages, bytes, time) rather than performing safe currency math. It is excellent for analytics dashboards, data visualization labels, or general utility formatting. Do not use it for currency calculations, as it relies on standard JavaScript numbers and will suffer from floating-point precision issues.
A node.js module to list and work on currency codes based on the ISO 4217 standard.
npm install currency-codes
var cc = require('currency-codes');
console.log(cc.code('EUR'));
/*
{
code: 'EUR',
number: 978,
digits: 2,
currency: 'Euro',
countries: [
'andorra', 'austria', 'belgium', 'cyprus', 'estonia', 'finland',
'france', 'germany', 'greece', 'ireland', 'italy', 'kosovo',
'luxembourg', 'malta', 'monaco', 'montenegro', 'netherlands',
'portugal', 'san marino', 'slovakia', 'slovenia', 'spain',
'vatican city' ]
}
*/
var cc = require('currency-codes');
console.log(cc.number(967));
/*
{
code: 'ZMW',
number: 967,
digits: 2,
currency: 'Zambian kwacha',
countries: [ 'zambia' ] }
*/
var cc = require('currency-codes');
console.log(cc.country('colombia'));
/*
[
{
code: 'COP',
number: 170,
digits: 2,
currency: 'Colombian peso',
countries: [ 'colombia' ]
}, {
code: 'COU',
number: 970,
digits: 2,
currency: 'Unidad de Valor Real',
countries: [ 'colombia' ]
}
]
*/
var cc = require('currency-codes');
console.log(cc.codes());
/*
[
'AED',
'AFN',
...
'ZAR',
'ZMW'
]
*/
var cc = require('currency-codes');
console.log(cc.numbers());
/*
[
'784',
'971',
...
'710',
'967'
]
*/
var cc = require('currency-codes');
console.log(cc.countries());
/*
[
'united arab emirates',
'afghanistan',
...
]
*/
var data = require('currency-codes/data');
console.log(data);
/*
[{
code: 'AED',
number: '784',
digits: 2,
currency: 'United Arab Emirates dirham',
countries: ['united arab emirates']
}, {
code: 'AFN',
number: '971',
digits: 2,
currency: 'Afghan afghani',
countries: ['afghanistan']
}, {
...
*/
var cc = require('currency-codes');
console.log(cc.publishDate);
/*
2024-06-25
*/
Fetch the latest copy of ISO-4217 from the maintainer and update this library's currency data file.
$ npm run iso
> currency-codes@2.1.0 iso
> npm run iso:fetch-xml && npm run iso:ingest-xml
> currency-codes@2.1.0 iso:fetch-xml
> node scripts/fetch-iso-4217-xml.js
Downloaded https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml to iso-4217-list-one.xml
> currency-codes@2.1.0 iso:ingest-xml
> node scripts/ingest-iso-4217-xml.js
Ingested iso-4217-list-one.xml into data.js
Wrote publish date to iso-4217-publish-date.js
Note: You may have to manually tweak the capitalization of some country's names.
MIT