JavaScript Number Format Library
hcg-number-formatter is a lightweight, zero-dependency JavaScript library for formatting numbers. It turns raw numbers into clean, human-readable strings - thousands separators, currency, compact abbreviations (K/M/B/T), ordinals (1st, 2nd, 3rd), file sizes, zero-padding, fixed decimals, and it can parse formatted strings back into numbers. It works in the browser with a single <script> tag and in Node.js via npm, with no dependencies and full TypeScript definitions included.
Table of Contents
What is hcg-number-formatter?
hcg-number-formatter is a small JavaScript utility that handles all the common ways you need to display numbers in a user interface. Instead of writing repetitive helper code every time you need to show a price, a follower count, a file size, or a ranking, you call one short function and get a correctly formatted string back.
It bundles eight focused functions - format, currency, compact, ordinal, fileSize, pad, fixed, and parse - each covering a real-world formatting task. The whole library is around 3 KB, has no dependencies, and runs the same way in the browser and in Node.js. It also fixes well-known JavaScript number pitfalls, such as (1.005).toFixed(2) returning "1.00" and large numbers like 1e21 printing in scientific notation.
Features
- Thousands separators with custom separator and decimal characters
- Currency formatting with 26 built-in currency codes
- Compact abbreviations: 1,500,000 to "1.5M"
- Ordinals: 1 to "1st", 22 to "22nd"
- Human-readable file sizes (IEC and SI)
- Zero-padding and fixed decimal places
- Parse formatted strings back into numbers (US and European)
- Zero dependencies, ~3 KB, works everywhere
Demo
Number formats demo
Normal function vs hcg-number-formatter
Plain JavaScript can format numbers, but it is verbose, easy to get wrong, and inconsistent across tasks. Here is the same work done with hand-written code versus hcg-number-formatter.
Thousands separators and decimals
// Plain JavaScript
(1234567.89).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}); // "1,234,567.89"
// hcg-number-formatter
format(1234567.89, { decimals: 2 }); // "1,234,567.89" Compact abbreviations (1.5M)
// Plain JavaScript - manual thresholds and rounding
function compact(n) {
if (n >= 1e9) return (n / 1e9).toFixed(1) + 'B';
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
return String(n);
}
compact(999999); // "1000.0K" (wrong - should roll over to 1M)
// hcg-number-formatter - boundary-safe
compact(999999); // "1M" Currency with a negative accounting style
// Plain JavaScript - no built-in accounting/parentheses style
const value = -500;
const text = '$' + Math.abs(value).toFixed(2);
const result = value < 0 ? '(' + text + ')' : text; // "($500.00)"
// hcg-number-formatter
currency(-500, { negativeStyle: 'parens' }); // "($500.00)" Rounding correctness
// Plain JavaScript - binary float rounding bug
(1.005).toFixed(2); // "1.00" (incorrect)
// hcg-number-formatter - decimal-safe rounding
fixed(1.005, { decimals: 2 }); // "1.01" Why use hcg-number-formatter?
- Less code, fewer bugs. One readable function call replaces blocks of manual math, threshold checks, and string building.
- Consistent everywhere. The same API and output run in the browser and Node.js, so server-rendered and client-rendered numbers match.
- Correct by default. It handles the cases plain JavaScript gets wrong: half-up decimal rounding, compact boundary rollover, very large and very small numbers, and accounting-style negatives.
- No dependencies, tiny footprint. About 3 KB with zero third-party packages, so it will not bloat your bundle or pull in a supply chain.
- Covers many tasks in one place. Currency, compact, ordinals, file sizes, padding, fixed decimals, and parsing - without reaching for several different libraries.
- Two-way.
parse()turns formatted strings back into numbers, including US and European styles, so user input round-trips cleanly.
hcg-number-formatter advantages
- Zero dependencies - nothing else to install or audit.
- Lightweight - roughly 3 KB, loads instantly.
- Universal - works via a single
<script>tag, CommonJSrequire, or ES moduleimport. - TypeScript ready - full type definitions included.
- 26 built-in currencies - USD, EUR, GBP, JPY, INR, and more, with correct symbols and decimal defaults.
- Locale aware - custom thousands separator and decimal character; auto-detects European input in
parse(). - Accurate rounding - decimal-safe, so financial values are not silently rounded down.
- Handles extremes - no scientific notation leaking into output for huge or tiny numbers.
- Tested - backed by an automated test suite covering edge cases.
- MIT licensed - free for personal and commercial use.
Comparison with other options
How hcg-number-formatter compares to the common ways people format numbers in JavaScript - the built-in Intl.NumberFormat and popular libraries such as numeral.js, numbro, and accounting.js.
| Capability | hcg-number-formatter | Intl.NumberFormat | numeral.js / numbro |
|---|---|---|---|
| Bundle size | ~3 KB, zero dependencies | Built in (0 KB) | ~9-12 KB, may add locales |
| Thousands separators | Yes, custom separator/decimal | Yes (locale based) | Yes |
| Currency with accounting negatives | Yes, parens style | Partial | Yes |
| Compact K/M/B/T | Yes, boundary-safe | Yes (notation: 'compact') | Yes |
| Ordinals (1st, 2nd) | Yes | No | Partial / plugin |
| File sizes (KiB/MiB) | Yes (IEC and SI) | No | Partial |
| Percent, roundTo, clamp, duration | Yes | Percent only | Partial |
| Parse formatted string to number | Yes (US and European) | No | Yes (numeral) |
| Decimal-safe rounding | Yes (1.005 to 1.01) | No (1.005 to 1.00) | Varies |
| TypeScript definitions | Included | Built in | Varies |
| License | MIT | Native API | MIT |
Intl.NumberFormat is great for locale-aware number and currency output and adds nothing to your bundle, but it does not handle ordinals, file sizes, durations, or parsing, and it inherits the binary rounding quirk. Libraries like numeral.js and numbro cover more, but are larger and less actively maintained. hcg-number-formatter aims for the middle: many real-world formatters in one tiny, dependency-free package with correct rounding.
Installation
Code links:
Node.js Usage
npm install hcg-number-formatter // CommonJS
const { format, currency, compact, ordinal, fileSize, pad, fixed, parse } = require('hcg-number-formatter');
// ES Modules
import { format, currency, compact, ordinal, fileSize, pad, fixed, parse } from 'hcg-number-formatter';
format(1234567.89); // "1,234,567.89" Vanilla JavaScript (browser)
Load the script from a CDN, then use the global NumberFormatter object.
<script src="https://cdn.jsdelivr.net/npm/hcg-number-formatter/index.js"></script> const { format, currency, compact, ordinal, fileSize, pad, fixed, percent, roundTo, clamp, duration, parse } = NumberFormatter;
console.log(format(1234567.89)); // "1,234,567.89"
currency(1234.5); // "$1,234.50"
compact(1500000); // "1.5M"
ordinal(3); // "3rd"
fileSize(1024); // "1 KiB"
pad(5, { length: 3 }); // "005"
fixed(3.14159, { decimals: 2 }); // "3.14"
percent(0.1234); // "12.34%"
roundTo(1234, { nearest: 50 }); // 1250
clamp(15, 0, 10); // 10
duration(3661); // "1h 1m 1s"
parse('$1,234.56'); // 1234.56
// or usage like this
// NumberFormatter.format(1234567); // "1,234,567"
API and Usage
format(number, options)
Adds thousands separators and optional decimal places.
format(1234567.89) // "1,234,567.89"
format(1234567, { decimals: 0 }) // "1,234,567"
format(1234.5, { decimals: 2 }) // "1,234.50"
format(1234.5, { separator: '.', decimal: ',' })// "1.234,5" (European)
format(-9876543.21) // "-9,876,543.21"
format(1e21) // "1,000,000,000,000,000,000,000" | Option | Type | Default | Description |
|---|---|---|---|
decimals | number | auto | Number of decimal places |
separator | string | ',' | Thousands separator |
decimal | string | '.' | Decimal point character |
currency(number, options)
Formats a number as currency.
currency(1234.5) // "$1,234.50"
currency(1234.5, { symbol: '€', position: 'after' }) // "1,234.50€"
currency(1234.5, { code: 'JPY' }) // "¥1,235"
currency(1234.5, { code: 'EUR' }) // "€1,234.50"
currency(1234.5, { code: 'EUR', separator: '.', decimal: ',' }) // "€1.234,50"
currency(-500, { negativeStyle: 'parens' }) // "($500.00)" | Option | Type | Default | Description |
|---|---|---|---|
symbol | string | '$' | Currency symbol |
position | 'before' | 'after' | 'before' | Symbol placement |
decimals | number | 2 | Decimal places |
code | string | - | ISO 4217 code — auto-sets symbol and decimals |
negativeStyle | 'minus' | 'parens' | 'minus' | How to show negative values |
separator | string | ',' | Thousands separator |
compact(number, options)
Abbreviates large numbers with K/M/B/T units.
compact(1200) // "1.2K"
compact(1500000) // "1.5M"
compact(2000000000) // "2B"
compact(999) // "999"
compact(1200, { precision: 2 }) // "1.20K"
compact(999999) // "1M" | Option | Type | Default | Description |
|---|---|---|---|
precision | number | 1 | Decimal places in output |
units | string[] | ['','K','M','B','T'] | Custom unit labels |
threshold | number | 1000 | Minimum value to abbreviate |
ordinal(number)
Converts a number to its ordinal string.
ordinal(1) // "1st"
ordinal(2) // "2nd"
ordinal(3) // "3rd"
ordinal(11) // "11th"
ordinal(21) // "21st"
ordinal(112) // "112th" fileSize(bytes, options)
Formats a byte count as a human-readable file size.
fileSize(0) // "0 Bytes"
fileSize(1024) // "1 KiB"
fileSize(1500000) // "1.43 MiB"
fileSize(1500000, { standard: 'si' }) // "1.5 MB"
fileSize(1073741824) // "1 GiB" | Option | Type | Default | Description |
|---|---|---|---|
standard | 'iec' | 'si' | 'iec' | iec=KiB/MiB (base-1024), si=KB/MB (base-1000) |
decimals | number | 2 | Decimal places |
pad(number, options)
Pads a number with leading characters to a fixed length.
pad(5, { length: 3 }) // "005"
pad(42, { length: 5 }) // "00042"
pad(7, { length: 4, char: '*' }) // "***7"
pad(12345, { length: 3 }) // "12345" | Option | Type | Default | Description |
|---|---|---|---|
length | number | 2 | Total output length |
char | string | '0' | Pad character |
fixed(number, options)
Formats a number to a fixed number of decimal places (with decimal-safe rounding).
fixed(3.14159, { decimals: 2 }) // "3.14"
fixed(3.1, { decimals: 4 }) // "3.1000"
fixed(1.005, { decimals: 2 }) // "1.01" | Option | Type | Default | Description |
|---|---|---|---|
decimals | number | 2 | Number of decimal places |
percent(number, options)
Formats a 0-1 ratio as a percentage. By default it multiplies by 100 and trims trailing zeros; pass scale: false when the value is already a percentage.
percent(0.1234) // "12.34%"
percent(0.1234, { decimals: 1 }) // "12.3%"
percent(1) // "100%"
percent(0.05, { decimals: 2 }) // "5.00%"
percent(25, { scale: false }) // "25%"
percent(-0.075) // "-7.5%" | Option | Type | Default | Description |
|---|---|---|---|
decimals | number | trim | Decimal places (default trims trailing zeros) |
scale | boolean | true | Multiply by 100 first; set false if the value is already a percentage |
space | boolean | false | Insert a space before the % |
separator | string | ',' | Thousands separator |
decimal | string | '.' | Decimal point character |
roundTo(number, options)
Rounds a number to the nearest step or to a number of decimal places. Returns a number, not a string.
roundTo(1234, { nearest: 50 }) // 1250
roundTo(1234, { nearest: 100 }) // 1200
roundTo(2.345, { decimals: 1 }) // 2.3
roundTo(1234, { nearest: 50, mode: 'floor' }) // 1200
roundTo(1201, { nearest: 50, mode: 'ceil' }) // 1250
roundTo(0.07, { nearest: 0.05 }) // 0.05 | Option | Type | Default | Description |
|---|---|---|---|
nearest | number | — | Round to the nearest multiple of this step |
decimals | number | 0 | Round to this many decimals (ignored when nearest is set) |
mode | 'round' | 'floor' | 'ceil' | 'round' | Rounding direction |
clamp(number, min, max)
Constrains a number to the inclusive range between min and max. Either bound may be omitted. Returns a number.
clamp(15, 0, 10) // 10
clamp(-5, 0, 10) // 0
clamp(7, 0, 10) // 7
clamp(3, 5) // 5 (min only)
clamp(100, undefined, 50) // 50 (max only) duration(seconds, options)
Formats a count of seconds as a human-readable duration in short, clock, or long style.
duration(3661) // "1h 1m 1s"
duration(90) // "1m 30s"
duration(3661, { style: 'clock' }) // "1:01:01"
duration(90, { style: 'clock' }) // "1:30"
duration(7200, { style: 'long' }) // "2 hours"
duration(93784) // "1d 2h 3m 4s" | Option | Type | Default | Description |
|---|---|---|---|
style | 'short' | 'clock' | 'long' | 'short' | Output style |
units | string[] | ['d','h','m','s'] | Which units to use |
parse(string, options)
Parses a formatted number string back into a number. Strips currency symbols, separators, and whitespace. Parentheses are treated as negative (accounting style).
parse('$1,234.56') // 1234.56
parse('1,000,000') // 1000000
parse('($500.00)') // -500
parse('1.234,56', { decimal: ',' }) // 1234.56 (European)
parse('abc') // NaN Options: separator, decimal (default "."; use "," for European format).
Frequently Asked Questions (FAQ)
What is hcg-number-formatter?
hcg-number-formatter is a small, zero-dependency JavaScript library for formatting numbers. It bundles focused functions - format, currency, compact, ordinal, fileSize, pad, fixed, percent, roundTo, clamp, duration, and parse - that turn raw numbers into clean, human-readable strings. It is about 3 KB and runs the same way in the browser and in Node.js.
Why use hcg-number-formatter instead of plain JavaScript?
Plain JavaScript number formatting is verbose and easy to get wrong. hcg-number-formatter replaces blocks of manual math with one readable function call, behaves consistently in the browser and Node.js, and fixes common pitfalls such as (1.005).toFixed(2) returning '1.00', compact boundary rollover, and scientific notation on very large or small numbers.
Is hcg-number-formatter free?
Yes. hcg-number-formatter is released under the MIT license and is free for both personal and commercial use.
Does hcg-number-formatter have any dependencies?
No. The library has zero third-party dependencies and is roughly 3 KB, so it does not bloat your bundle or add supply-chain risk.
How do I install hcg-number-formatter?
Install it from npm with 'npm install hcg-number-formatter', or load it in the browser with a single script tag from a CDN and use the global NumberFormatter object.
Does hcg-number-formatter support currencies and locales?
Yes. It includes 26 built-in currency codes such as USD, EUR, GBP, JPY, and INR with correct symbols and decimal defaults, supports custom thousands separators and decimal characters, and auto-detects European number input when parsing.