npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

friendly-number

v1.1.0

Published

A TypeScript library for creating, converting, and formatting friendly 'clean' numbers

Downloads

24

Readme

friendly-numbers

A TypeScript library for displaying and generating "friendly" numbers in UI. Built on Intl.NumberFormat, it provides automatic decimal handling, unified formatting options, and tools for generating human-friendly numbers and scales—e.g. for clean UI interpolation from user input, chart axes tick marks, or data visualization.

What It Does

This library provides a convenience layer over Intl.NumberFormat with opinionated, sensible defaults for common use cases:

1. Intelligent Decimal Handling

Problem: Raw Intl.NumberFormat requires you to manually specify decimal places for every number, leading to either:

  • Too many decimals for large numbers: 1,234.123 instead of 1,234
  • Too few decimals for small numbers: 0 instead of 0.123

Solution: Auto decimals that adjust based on magnitude:

// With this library (auto decimals)
formatFriendlyNumber(1234.56);  // "1,235" (0 decimals for large numbers)
formatFriendlyNumber(12.34);    // "12.3"  (1 decimal)
formatFriendlyNumber(1.234);    // "1.23" (2 decimals)
formatFriendlyNumber(0.00123);   // "0.0012" (4 decimals)

// With raw Intl.NumberFormat - you'd need different configs for each
new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(1234.56); // "1,235"
new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 }).format(12.34);   // "12.3"

2. Unified API for Common Formatting Patterns

Problem: Intl.NumberFormat has many granular options that are confusing to combine:

  • notation, compactDisplay, currencyDisplay, unitDisplay, signDisplay, etc.

Solution: Single intuitive display option that maps sensibly:

// This library - simple and intuitive
formatFriendlyNumber(1234567, { currency: 'USD', display: 'short' }); // "$1.23M"

// Raw Intl - verbose and error-prone
new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  notation: 'compact',
  compactDisplay: 'short',
  currencyDisplay: 'narrowSymbol',
  minimumFractionDigits: 0,
  maximumFractionDigits: 2
}).format(1234567);

3. Scientific Notation for Very Small Numbers

Problem: Compact notation doesn't work for numbers < 1, leaving you with ugly decimals like 0.00012

Solution: Automatic scientific notation for small numbers with display: 'short':

formatFriendlyNumber(0.00012, { display: 'short' }); // "1.2E-4"

4. Significant Digits Preservation in Scaling

Problem: When converting units (days→years, meters→km), you lose precision context

Solution: Intelligent significant digit preservation:

scaleNumber(7, 365.25);  // 3000 (preserves 1 sig digit from 7)
scaleNumber(2.5, 16);    // 40 (preserves 2 sig digits from 2.5)

5. Friendly Scale Generation

Problem: Creating chart axes or slider ticks requires manual calculation of "nice" numbers

Solution: Automatic generation of human-friendly scales:

generateScale(100, 1_000_000);
// [100, 200, 500, 1000, 2000, 5000, 10000, 25000, 50000, 100000, 200000, 500000, 1000000]
// All "round" numbers that humans expect

Installation

npm install friendly-numbers

Quick Start

import {
  countSignificantDigits,
  roundToSignificantDigits,
  formatFriendlyNumber,
  scaleNumber,
  generateScale
} from 'friendly-numbers';

// Count significant digits
countSignificantDigits(1000);      // 1
countSignificantDigits(1200);      // 2
countSignificantDigits(0.00012);   // 2

// Round to significant digits
roundToSignificantDigits(1234, 2); // 1200

// Format numbers (auto decimals by default)
formatFriendlyNumber(1234.56);    // "1,235"
formatFriendlyNumber(1234567, { display: 'short' }); // "1.2M"
formatFriendlyNumber(1234.56, { currency: 'USD' }); // "$1,234.56"
formatFriendlyNumber([100, 200]); // "100–200" (range)
formatFriendlyNumber(0.1234, { percent: true }); // "12%"

// Scale with precision preservation
scaleNumber(7, 365.25); // ~2600 (days to years, preserving sig digits)

// Generate friendly scales
generateScale(100, 1_000_000);
// [100, 200, 500, 1000, 2000, 5000, 10000, 25000, 50000, 100000, 200000, 500000, 1000000]

API Reference

Significant Digits

countSignificantDigits(value: number): number

Counts the number of significant digits in a number.

countSignificantDigits(1000);    // 1
countSignificantDigits(1200);    // 2
countSignificantDigits(1234);    // 4
countSignificantDigits(0.00012); // 2

roundToSignificantDigits(value: number, significantDigits: number): number

Rounds a number to the specified number of significant digits.

roundToSignificantDigits(1234, 2); // 1200
roundToSignificantDigits(1234, 1); // 1000
roundToSignificantDigits(0.001234, 2); // 0.0012

roundToHalfSignificantDigits(value: number, significantDigits: number): number

Rounds to significant digits with "half" rounding (rounds to 0 or 5).

roundToHalfSignificantDigits(1321, 1); // 1500
roundToHalfSignificantDigits(1234, 2); // 1200

toSignificantDigits(value: number, significantDigits: number, options?: SignificantDigitsOptions): number

Flexible rounding with mode selection.

toSignificantDigits(1234, 2);                    // 1200 (full mode)
toSignificantDigits(1321, 1, { mode: 'half' }); // 1500 (half mode)

Number Scaling

scaleNumber(value: number, ratio: number, options?: ScaleOptions): number

Scales a number by a ratio while intelligently preserving significant digits.

// Convert days to years
scaleNumber(7, 365.25); // ~2600

// Convert pounds to ounces
scaleNumber(2.5, 16); // 40

// Custom options
scaleNumber(100, 1.5, {
  preserveSignificantDigits: true,
  minSignificantDigits: 2,
  maxSignificantDigits: 4
});

Options:

  • preserveSignificantDigits (default: true): Preserve significant digits during scaling
  • minSignificantDigits (default: 1): Minimum significant digits to preserve
  • maxSignificantDigits (default: 15): Maximum significant digits to preserve

scaleNumberInverse(value: number, ratio: number, options?: ScaleOptions): number

Scales a number by the inverse of a ratio.

scaleNumberInverse(40, 16); // 2.5 (ounces to pounds)

Formatting

formatFriendlyNumber(value: number | [number, number], options?: FriendlyNumberOptions): string

A single, intuitive function for formatting numbers and ranges with sensible defaults and automatic intelligent decimal handling.

// Basic formatting (auto decimals by default)
formatFriendlyNumber(1234.56);  // "1,235"
formatFriendlyNumber(12.34);    // "12.3"
formatFriendlyNumber(1.234);    // "1.23"

// Compact notation (K, M, B, etc.)
formatFriendlyNumber(1234567, { display: 'short' });   // "1.2M"
formatFriendlyNumber(1234567, { display: 'long' }); // "1.2 million"

// Scientific notation for very small numbers (0 < |n| < 1)
formatFriendlyNumber(0.00012, { display: 'short' }); // "1.2E-4"
formatFriendlyNumber(0.00012, { display: 'standard' }); // "0.00012"

// Currency
formatFriendlyNumber(1234.56, { currency: 'USD' }); // "$1,234.56"
formatFriendlyNumber(1234567, { currency: 'USD', display: 'short' }); // "$1.2M"
formatFriendlyNumber(1234, { currency: 'USD', code: true }); // "1,234.00 USD"
formatFriendlyNumber(-1234, { currency: 'USD', accounting: true }); // "($1,234.00)"

// Sign display
formatFriendlyNumber(42, { sign: true });  // "+42"
formatFriendlyNumber(-42, { sign: true }); // "-42"
formatFriendlyNumber(0, { sign: true });   // "0"

// Absolute value (hide sign)
formatFriendlyNumber(-42, { absolute: true }); // "42"
formatFriendlyNumber(-1234, { currency: 'USD', absolute: true }); // "$1,234"

// Units
formatFriendlyNumber(50, { unit: 'kilometer-per-hour' }); // "50 km/h"
formatFriendlyNumber(50, { unit: 'kilometer-per-hour', display: 'long' }); // "50 kilometers per hour"

// Percent
formatFriendlyNumber(0.1234, { percent: true }); // "12%"
formatFriendlyNumber(0.1234, { percent: true, decimals: 1 }); // "12.3%"

// Significant digits
formatFriendlyNumber(1234.56, { significantDigits: 2 }); // "1,200"

// Decimal control
formatFriendlyNumber(1234.5678, { decimals: 2 });  // "1,234.57" (fixed)
formatFriendlyNumber(1234.5, { decimals: { min: 0, max: 2 } }); // "1,234.5"
formatFriendlyNumber(1234, { decimals: 'auto' }); // "1,234" (intelligent, default)

// Ranges (uses Intl.NumberFormat.formatRange)
formatFriendlyNumber([100, 200]);                           // "100–200"
formatFriendlyNumber([1234, 5678], { significantDigits: 2 }); // "1,200–5,700"
formatFriendlyNumber([100, 200], { currency: 'USD' });      // "$100.00–$200.00"
formatFriendlyNumber([1000000, 5000000], { display: 'short' }); // "1M–5M"

// Custom format (full control)
formatFriendlyNumber(1234.5678, {
  format: {
    style: 'decimal',
    minimumFractionDigits: 3,
    maximumFractionDigits: 3
  }
}); // "1,234.568"

Options:

  • locale: Locale string(s) for formatting (default: 'en-US')
  • display: Unified display style - 'standard' | 'short' | 'long'
    • 'standard': Standard notation (1,234) - for currency: symbol ($)
    • 'short': Compact notation with short display (1.2M), scientific notation for very small numbers (1.2E-4) - for currency: narrow symbol ($), for units: short (km/h)
    • 'long': Compact notation with long display (1.2 million) - for currency: full name (US dollars), for units: long (kilometers per hour)
  • sign: Show sign for positive/negative numbers - boolean (default: false, true = exceptZero)
  • absolute: Show absolute value (removes sign display) - boolean (default: false)
  • currency: Currency code (e.g., 'USD', 'EUR')
  • code: Show currency code instead of symbol - boolean (default: false, only applies to currency)
  • accounting: Use accounting format for negative currency - boolean (default: false)
  • unit: Unit identifier (e.g., 'kilometer-per-hour', 'celsius')
  • percent: Format as percentage - boolean (default: false)
  • significantDigits: Round to significant digits before formatting - number
  • decimals: Decimal control - 'auto' | number | { min?: number; max?: number } (default: 'auto')
    • 'auto': Intelligent decimals based on magnitude
    • number: Fixed decimal places
    • { min, max }: Min/max decimal places
  • format: Custom Intl.NumberFormatOptions for full control

Scale Generation

generateScale(start: number, end: number, options?: GenerateScaleOptions): number[]

Generate a scale of numbers between start and end with various progression modes.

// Friendly progression (default)
generateScale(100, 1_000_000);
// [100, 200, 500, 1000, 2000, 5000, 10000, 25000, 50000, 100000, 200000, 500000, 1000000]

// Linear progression
generateScale(0, 100, { progression: 'linear', steps: 10 });
// [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

// Exponential progression
generateScale(1, 1000, { progression: 'exponential', steps: 3 });
// [1, 10, 100, 1000]

Options:

  • progression: 'linear' | 'exponential' | 'friendly' (default: 'friendly')
  • rounding: 'none' | 'significant' | 'half' | 'friendly' (default: 'friendly')
  • significantDigits: Number of significant digits for rounding (default: 2)
  • steps: Number of intermediate steps (default: 10)
  • includeStart: Include start value (default: true)
  • includeEnd: Include end value (default: true)

generateScaleFromBaseline(baseline: number, count: number, options?: GenerateScaleOptions): number[]

Generate a scale starting from a baseline value.

// Friendly progression
generateScaleFromBaseline(100, 5);
// [100, 200, 500, 1000, 2000]

// Linear progression
generateScaleFromBaseline(10, 5, { progression: 'linear' });
// [10, 20, 30, 40, 50]

// Exponential progression
generateScaleFromBaseline(1, 4, { progression: 'exponential' });
// [1, 10, 100, 1000]

Use Cases

UI Number Display

import { formatFriendlyNumber } from 'friendly-numbers';

// Display large numbers compactly
const followers = formatFriendlyNumber(1234567, { display: 'short' }); // "1.2M"

// Show deltas with signs
const change = formatFriendlyNumber(42.5, { sign: true }); // "+42.5"

// Currency with auto decimals
const price = formatFriendlyNumber(1299, { currency: 'USD' }); // "$1,299"

// Percentages
const conversionRate = formatFriendlyNumber(0.1234, { percent: true, decimals: 1 }); // "12.3%"

Unit Conversions

import { scaleNumber } from 'friendly-numbers';

// Days to weeks (preserving appropriate precision)
const weeks = scaleNumber(7, 1/7); // 1

// Pounds to ounces
const ounces = scaleNumber(2.5, 16); // 40

// Meters to kilometers
const km = scaleNumber(1500, 0.001); // 1.5

Chart Axes & Scales

import { generateScale, formatFriendlyNumber } from 'friendly-numbers';

// Generate friendly tick marks
const ticks = generateScale(0, 1_000_000);
// [0, 100, 200, 500, 1000, 2000, 5000, 10000, ...]

// Format for display
const labels = ticks.map(t => formatFriendlyNumber(t, { display: 'short' }));
// ["0", "100", "200", "500", "1K", "2K", "5K", "10K", ...]

Data Rounding

import { roundToSignificantDigits } from 'friendly-numbers';

// Clean up noisy data
const cleanValue = roundToSignificantDigits(1234.56789, 3); // 1230

TypeScript Support

This library is written in TypeScript and includes full type definitions.

import type {
  FriendlyNumberOptions,
  ScaleOptions,
  GenerateScaleOptions,
  SignificantDigitsOptions
} from 'friendly-numbers';

Browser Support

This library uses Intl.NumberFormat, which is supported in all modern browsers. For older browsers, you may need a polyfill.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.