anyamount API reference
Overview
anyamount is a tiny number formatter built entirely on the native Intl.NumberFormat browser API. One function, one options object, three modes — plus a helper for bare currency symbols. The 1.x API is stable: new options arrive in minors, breaking changes only in majors.
The browser already knows how to format numbers, money, and units in 200+ languages. anyamount just makes that API pleasant to use.
import { anyamount } from 'anyamount'
anyamount(1234567)
// "1.2M" — smart mode (default)
anyamount(1999, { mode: 'currency', currency: 'EUR' })
// "€1,999.00"
anyamount(3.2, { mode: 'unit', unit: 'gigabyte' })
// "3.2 GB"Install
npm install anyamount
# or
pnpm add anyamount
# or
yarn add anyamountOr take the whole family at once with npm install anyfamily.
anyamount()
The single entry point. Pass a number, optionally pass options.
anyamount(value)
anyamount(value, options?)
anyamount(1234567)
// runtime locale, smart mode
anyamount(9999, { locale: 'en' })
// "9,999" — below the compact cutoff
anyamount(1234567, { locale: 'en', style: 'long' })
// "1.2 million"
anyamount(1999.99, { mode: 'currency', currency: 'EUR', locale: 'en', digits: 0 })
// "€2,000"Migrating from 1.x
2.0 removed the separate anyamountPartsand the other extra exports — they are the same functions and values, reached through the one name the package exports.
- import { anyamount, anyamountParts, anyamountSymbol } from 'anyamount'
+ import { anyamount } from 'anyamount'
- anyamountParts(1999, opts)
+ anyamount.parts(1999, opts)
- anyamountSymbol('USD')
+ anyamount.symbol('USD')Arguments, return values and throwing behaviour are unchanged, and nothing else in the API moved. Every any* package follows this shape from 2.0 on: the bare call does the job, everything else hangs off the same name.
anyamount.parts()
Same arguments as anyamount(), but returns the Intl.NumberFormat.formatToParts output unchanged — style the number apart from the currency symbol or unit, or rebuild the output your own way.
import { anyamount } from 'anyamount'
anyamount.parts(1999, { mode: 'currency', currency: 'EUR', locale: 'en' })
// [
// { type: 'currency', value: '€' },
// { type: 'integer', value: '1' },
// { type: 'group', value: ',' },
// { type: 'integer', value: '999' },
// { type: 'decimal', value: '.' },
// { type: 'fraction', value: '00' },
// ]
// React: shrink the currency symbol
anyamount.parts(price, { mode: 'currency', currency: 'EUR' }).map((p, i) =>
p.type === 'currency' ? <small key={i}>{p.value}</small> : p.value,
)Note: part values keep the original Intl characters — the space between number and unit can be U+00A0 or U+202F (no-break spaces) depending on locale and ICU version.
anyamount.symbol()
Resolves an ISO 4217 code to its localized symbol, with no number attached — for labels, currency pickers, and input affixes, where the amount is rendered separately (or not at all).
import { anyamount } from 'anyamount'
anyamount.symbol('USD', { locale: 'en' }) // "$"
anyamount.symbol('EUR', { locale: 'en' }) // "€"
anyamount.symbol('GBP', { locale: 'en' }) // "£"
anyamount.symbol('JPY', { locale: 'ja' }) // "¥"
anyamount.symbol('RUB', { locale: 'ru' }) // "₽"
anyamount.symbol('USD', { locale: 'en', display: 'code' }) // "USD"
anyamount.symbol('USD', { locale: 'en', display: 'name' }) // "US dollars"display defaults to 'narrowSymbol' — the bare symbol, never the disambiguated US$some locales prefer. Codes with no symbol in the locale's data come back as the code itself.
Note: a malformed code throws a RangeError straight from Intl — 'US' is not a currency. Formatting a full amount? Stay in currency mode with currencyDisplay; this is the escape hatch for when there is no amount.
Modes
The mode option picks the rendering strategy. Each mode reads only the options that apply to it — the rest are ignored.
smart (default)
Compact notation for big numbers, plain formatting for small ones. The cutoff is |value| >= 10000.
reads: locale, style, digits
currency
Money via the Intl.NumberFormat currency style. currency is required — any ISO 4217 code. Missing it throws a TypeError.
anyamount(1999, { mode: 'currency', currency: 'EUR', locale: 'en' })
// "€1,999.00"
anyamount(1999, { mode: 'currency', currency: 'RSD', locale: 'sr' })
// "1.999,00 RSD"
anyamount(1999, { mode: 'currency', currency: 'JPY', locale: 'ja' })
// "¥1,999" — JPY has no minor unit, Intl knows
anyamount(1999.99, { mode: 'currency', currency: 'EUR', locale: 'en', digits: 0 })
// "€2,000"currencyDisplay picks how the currency itself is spelled — symbol by default, opt into anything else.
anyamount(1999, { mode: 'currency', currency: 'USD', locale: 'en' })
// "$1,999.00" — 'symbol' (default)
anyamount(1999, { mode: 'currency', currency: 'USD', locale: 'en-CA', currencyDisplay: 'narrowSymbol' })
// "$1,999.00" — bare symbol, where the locale would print "US$"
anyamount(1999, { mode: 'currency', currency: 'USD', locale: 'en', currencyDisplay: 'code' })
// "USD 1,999.00"
anyamount(1999, { mode: 'currency', currency: 'USD', locale: 'en', currencyDisplay: 'name' })
// "1,999.00 US dollars"reads: locale, currency, currencyDisplay, digits
unit
Measurements via the Intl.NumberFormat unit style. unit is required — any sanctioned identifier, including compound -per- pairs. Missing it throws a TypeError.
anyamount(3.2, { mode: 'unit', unit: 'gigabyte', locale: 'en' })
// "3.2 GB"
anyamount(120, { mode: 'unit', unit: 'kilometer-per-hour', locale: 'en' })
// "120 km/h"
anyamount(3.2, { mode: 'unit', unit: 'gigabyte', locale: 'en', style: 'long' })
// "3.2 gigabytes"
anyamount(5, { mode: 'unit', unit: 'kilometer', locale: 'en', style: 'narrow' })
// "5km"reads: locale, unit, style, digits
Units
Intl supports a fixed, sanctioned list of unit identifiers (from ECMA-402), plus any <unit>-per-<unit> compound of them. anyamount ships the full list as a TypeScript union, so invalid units fail at compile time.
acre bit byte celsius centimeter day degree fahrenheit
fluid-ounce foot gallon gigabit gigabyte gram hectare hour
inch kilobit kilobyte kilogram kilometer liter megabit
megabyte meter microsecond mile mile-scandinavian milliliter
millimeter millisecond minute month nanosecond ounce percent
petabyte pound second stone terabit terabyte week yard year// compounds work too
anyamount(120, { mode: 'unit', unit: 'kilometer-per-hour' }) // "120 km/h"
anyamount(8.5, { mode: 'unit', unit: 'liter-per-kilometer' }) // "8.5 L/km"
anyamount(2, { mode: 'unit', unit: 'meter-per-second' }) // "2 m/s"Options
mode'smart' | 'currency' | 'unit'default: 'smart'Rendering strategy. Each mode reads only the options that apply to it.
localestring | string[]default: runtime localeAny valid BCP 47 locale tag, or a fallback array — 'en', 'en-US', 'zh-TW', ['sr-Latn-RS', 'en'].
currencystringCurrency mode only, required. Any ISO 4217 code — 'EUR', 'USD', 'JPY', 'RSD'.
currencyDisplay'symbol' | 'narrowSymbol' | 'code' | 'name'default: 'symbol'Currency mode only. How the currency is spelled: '$1,999.00', 'USD 1,999.00', or '1,999.00 US dollars'. 'narrowSymbol' keeps the bare '$' where a locale would print 'US$'.
unitUnitUnit mode only, required. A sanctioned unit identifier or a compound '<unit>-per-<unit>' pair. Typed as a union — your editor autocompletes it.
style'long' | 'short' | 'narrow'default: 'short'Smart and unit modes. Wording length: '1.2M' vs '1.2 million', '3.2 GB' vs '3.2 gigabytes'.
digitsnumberdefault: per modemaximumFractionDigits — a ceiling, not a fixed width: trailing zeros are not padded on, so digits: 2 renders 2.5, not 2.50. Defaults: smart — 2 plain / 1 compact, unit — 2, currency — the currency's own (which it keeps as a minimum).
What breaks without this
Every one of these is an assumption baked into a hand-written formatter, and each is wrong somewhere.
The symbol is not always in front
'$' + value.toFixed(2) is a German price written backwards: de-DE puts the symbol last and swaps both separators — 1.999,00 €. Two locales sharing a currency need not write it the same way.
toFixed is not money rounding
It rounds a binary double, so the half-way cases go where the bits fall rather than where accounting expects. It also assumes two decimals, which JPY does not have and KWD exceeds — the right number of digits is a fact about the currency, and Intl already knows it.
Compact notation is not K and M
1.2M is English. Russian writes 1,2 млн; Japanese groups by ten-thousands and writes 123万, a different place value, not a translated suffix. A K/M/B table cannot be localized into being correct.
Units are a closed list, and they inflect
Writing "3.2 GB" by hand skips the part where the unit name agrees with the number and the locale. Intl takes a sanctioned list of units and handles both — outside that list there is no localized name to be had, from any library.
Recipes
Copy, paste, move on.
// Dashboard stat
anyamount(views, { locale: 'en' })
// "1.2M"
// …spelled out
anyamount(views, { locale: 'en', style: 'long' })
// "1.2 million"
// Price
anyamount(product.cents / 100, { mode: 'currency', currency: 'EUR', locale: 'de' })
// "1.999,00 €"
// Price with no cents
anyamount(total, { mode: 'currency', currency: 'EUR', digits: 0 })
// "€2,000"
// Storage meter
anyamount(file.gb, { mode: 'unit', unit: 'gigabyte' })
// "3.2 GB"
// Speed, compound unit
anyamount(120, { mode: 'unit', unit: 'kilometer-per-hour', locale: 'ru' })
// "120 км/ч"
// Currency affix inside an input, amount rendered separately
anyamount.symbol(account.currency)
// "$" React / Next.js
anyamount is pure and synchronous, so it works in a component as-is. Whatanyfamily-react adds is a shared locale: set it once onAnyfamilyProvider and every hook below picks it up, so you do not thread locale through every call.
import { AnyfamilyProvider, useAnyamount } from 'anyfamily-react'
function Price({ cents }: { cents: number }) {
return <b>{useAnyamount(cents / 100, { mode: 'currency', currency: 'EUR' })}</b>
}
<AnyfamilyProvider locale="de">
<Price cents={199900} />
</AnyfamilyProvider>`useAnyamountSymbol` is there too, for the bare currency symbol.
Locales
Same calls in a few languages — no extra setup, no locale files.
// smart mode
anyamount(1234567, { locale: 'ru' }) // "1,2 млн"
anyamount(1234567, { locale: 'de' }) // "1,2 Mio."
anyamount(1234567, { locale: 'ja' }) // "123.5万"
// currency mode
anyamount(1999, { mode: 'currency', currency: 'USD', locale: 'de' })
// "1.999,00 $"
anyamount(1999, { mode: 'currency', currency: 'INR', locale: 'hi' })
// "₹1,999.00"
// unit mode
anyamount(120, { mode: 'unit', unit: 'kilometer-per-hour', locale: 'ru' })
// "120 км/ч"Pass any valid BCP 47 language tag — including regional variants like en-GB, zh-TW, or pt-BR. Locale is optional; when omitted, native Intl uses the runtime locale. Fallback arrays like ['sr-Latn-RS', 'en'] also work.
Output is pure — no clock reads, no environment sniffing — so server and client render identically. SSR-safe by construction.
vs the alternatives
What you would otherwise reach for, and what changes if you do.
| anyamount | numeral.js | accounting.js | |
|---|---|---|---|
| locale data bundled | none (Intl) | one file per locale | none, you configure it |
| locales | 200+ | registered by hand | whatever you pass |
| currency rules | from the currency | manual symbol | manual symbol |
| decimal digits | per currency | manual | manual |
| units | sanctioned list | no | no |
| compact notation | every locale | English forms | no |
| dependencies | 0 | 0 | 0 |
anyamount is 0.9kb gzipped and formats numbers. It is not a money type: it does not add prices, hold exchange rates, or protect you from floating-point arithmetic. Do the arithmetic in minor units or in a decimal library, then hand the result here to be written down.
Compatibility
anyamount uses Intl.NumberFormat with compact notation and unit support — widely available since 2020.
Limitations
A few things worth knowing before you ship:
No byte auto-scaling yet
anyamount(3200000000, { mode: 'unit', unit: 'byte' }) will not pick GB for you — pass the unit you want. Automatic scaling is planned for a future minor.
Output depends on the runtime's Intl data
anyamount delegates all formatting to native Intl. Exact output — separators, spacing, compact suffixes — may vary between Node versions, browsers, and OSes. Don't hardcode expected strings in tests; use pattern matching instead.
Sanctioned units only
Intl supports a fixed list of unit identifiers and -per- compounds of them. There is no way to format arbitrary custom units — that's an Intl constraint, not an anyamount one.
Deliberately small
One function, three modes, on purpose. No percent mode, no ranges, no parsing. anyamount follows semver — the 1.x API is stable, new options arrive in minors, breaking changes only in majors.