Tiny human-readable number formatter built on native Intl.
Turn numbers into "1.2M", "€1,999.00", "3.2 GB", or "120 km/h".
One function. Smart defaults. Any locale. ~0.7kb gzip. Zero dependencies.
Intl.NumberFormat is powerful. anyamount makes it usable.
Built for dashboards, feeds, pricing pages, file lists, and stats — anywhere a raw number should read like a person wrote it. No locale files. No plugins. No config.
import { anyamount } from "anyamount";
anyamount(1234567);
// "1.2M" — smart mode (default)
anyamount(42);
// "42"
anyamount(1999, { mode: "currency", currency: "EUR" });
// "€1,999.00"
anyamount(3.2, { mode: "unit", unit: "gigabyte" });
// "3.2 GB"
anyamount(120, { mode: "unit", unit: "kilometer-per-hour", locale: "ru" });
// "120 км/ч"npm install anyamountanyamount(value);
anyamount(value, options);value is a number or a bigint — every mode accepts both. ±Infinity
formats as the locale's infinity symbol ("∞"); NaN throws.
anyamount(1234567);
anyamount(0.1234);
anyamount(-42.5);
anyamount(123456789012345678901n); // beyond MAX_SAFE_INTEGER, no precision lossThe mode option picks the rendering strategy. Default is "smart".
Context-aware. Compact notation for big numbers, plain formatting for small
ones — the cutoff is |value| >= 10000.
anyamount(1234567, { locale: "en" }); // "1.2M"
anyamount(10000, { locale: "en" }); // "10K"
anyamount(9999, { locale: "en" }); // "9,999"
anyamount(42, { locale: "en" }); // "42"
anyamount(0.1234, { locale: "en" }); // "0.12"
anyamount(1234567, { locale: "en", style: "long" });
// "1.2 million"
anyamount(1234567, { locale: "en", digits: 2 });
// "1.23M"Fraction digits default to 2 for plain numbers and 1 for compact ones.
Reads: locale, style, digits.
Money via the Intl.NumberFormat currency style. currency is required —
any ISO 4217 code.
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"Fraction digits default to the currency's own (2 for EUR, 0 for JPY).
Reads: locale, currency, digits.
Measurements via the Intl.NumberFormat unit style. unit is required —
any sanctioned identifier, including compound "<unit>-per-<unit>" pairs.
The unit option is typed as a union, so your editor autocompletes it.
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"Fraction digits default to 2.
Reads: locale, unit, style, digits.
| Option | Type | Default | Used by |
|---|---|---|---|
mode |
"smart" | "currency" | "unit" |
"smart" |
— |
locale |
string | string[] |
runtime locale | all |
currency |
string (ISO 4217) |
— (required) | currency |
unit |
sanctioned unit identifier | — (required) | unit |
style |
"long" | "short" | "narrow" |
"short" |
smart, unit |
digits |
number (max fraction digits) |
smart default | all |
The options type is a discriminated union on mode — TypeScript requires
currency in currency mode and unit in unit mode at compile time, and
rejects options that don't belong to the mode. From plain JavaScript, the
same rules hold at runtime: a missing currency or unit throws a clear
TypeError, and stray options are ignored.
anyamountParts() accepts the same arguments as anyamount() and returns the
Intl.NumberFormat.formatToParts output unchanged — style the number apart
from the currency symbol or unit, or rebuild the string your own way.
import { anyamountParts } from "anyamount";
anyamountParts(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
anyamountParts(price, { mode: "currency", currency: "EUR" }).map((p, i) =>
p.type === "currency" ? <small key={i}>{p.value}</small> : p.value,
);Pass any valid BCP 47 tag — including regional variants like en-GB, zh-TW,
pt-BR. Fallback arrays also work.
anyamount(1234567, { locale: "ru" }); // "1,2 млн"
anyamount(1234567, { locale: "de" }); // "1,2 Mio."
anyamount(1234567, { locale: "ja" }); // "123.5万"
anyamount(1234567, { locale: ["sr-Latn-RS", "en"] });
anyamount(1999, { mode: "currency", currency: "USD", locale: "de" });
// "1.999,00 $"When omitted, native Intl uses the runtime locale.
Output is pure — no Date.now(), no environment reads — so server and client
render identically. SSR-safe by construction.
| anyamount | pretty-bytes | filesize | numeral | |
|---|---|---|---|---|
| gzip | ~0.7kb | ~1kb | ~3kb | ~5kb |
| currency | yes | no | no | yes |
| units beyond bytes | yes | no | no | no |
| localized output | 200+ locales | partial | partial | manual locale files |
| dependencies | 0 | 0 | 0 | 0 |
Honest ones:
- No byte auto-scaling yet.
anyamount(3200000000, { mode: "unit", unit: "byte" })will not pickGBfor you — pass the unit you want. Auto-scaling is planned for a future minor. - No percent mode, no ranges, no parsing. Deliberately one function, three modes.
- Exact output strings come from
Intland may vary between ICU versions — don't snapshot them across environments. - Sanctioned units only.
Intlsupports a fixed list of unit identifiers (and-per-compounds of them) — no arbitrary custom units.
anyamount follows semver. The 1.x API is stable:
new options arrive in minors, breaking changes only in majors. Exact
formatted strings come from Intl and may vary between ICU versions, so
never assert on them across environments.
Node.js 18+ · Chrome 77+ · Firefox 78+ · Safari 14.1+ · Edge Runtime · Cloudflare Workers · Deno
CI runs the full suite on Node 20, 22, and 24.
