Dinero.js

website·Indexed 33 days ago

https://www.dinerojs.com/

An open-source library for handling monetary values. It provides a set of standalone functions for creating Dinero objects, performing mutations like allocate, subtract, and multiply, and handling currency conversions, comparisons, and formatting. The library supports ISO 4217 currencies, TypeScript type safety, and provides guides for precision, large numbers, and database storage.

Tokens
25.8K
Snippets
136
Records
176
Agent score
65%

What's inside Dinero.js

  1. Understand Dinero.js functional API design

    Dinero.js utilizes standalone functions (e.g., add(d1, d2)) rather than chainable methods (e.g., d1.add(d2)). This design choice ensures modularity and allows modern bundlers to perform effective tree-shaking, eliminating unused operations from the final bundle. In contrast, a prototype-based method approach would force every Dinero object to carry all available operations regardless of whether they are used.
    // Functional style used by Dinero.js
    add(d1, d2);
  2. Understand and use Scale for monetary precision

    The scale in Dinero.js expresses precision to ensure monetary values are represented accurately without losing data during calculations (e.g., when applying fractional tax rates). By default, the scale is equal to the currency exponent. However, Dinero.js can automatically increase the scale during operations or allow developers to specify a manual scale to represent values smaller than the currency's smallest unit.
    import { dinero, toSnapshot } from 'dinero.js';
    import { USD } from 'dinero.js/currencies';
    
    // Default scale is picked up from USD.exponent (2)
    const d = dinero({ amount: 5000, currency: USD });
    const { scale } = toSnapshot(d); // scale is 2
  3. Dinero.js Core Principles

    Dinero.js is designed with the following constraints and features:

    • Immutability: All operations return new Dinero objects; original values are never mutated.
    • Precision: Money is handled as integers in minor units to prevent floating-point math issues.
    • Multi-currency: Includes built-in ISO 4217 currencies with compile-time safety to prevent currency mismatches.
    • Tree-shakeable: The functional API allows developers to import only the specific functions needed, optimizing bundle size.
  4. Tree-shake Dinero.js to reduce bundle size

    Dinero.js is a pure library without side-effects, making it compatible with tree-shaking in modern build systems. To eliminate unused code from your final bundle, import only the specific functions and currencies you need rather than the entire library.
    import { dinero, add, subtract } from 'dinero.js';
    import { USD } from 'dinero.js/currencies';
    
    // Only these functions and their dependencies end up in your bundle
  5. Handle large monetary amounts with BigInt

    When representing colossal amounts of money (e.g., world debt) or cryptocurrencies with high exponents (e.g., Ether), the default JavaScript number type may lose precision. Dinero.js provides a dedicated BigInt implementation via the dinero.js/bigint subpath.

    Critical: You must use currencies imported from dinero.js/bigint/currencies because standard currencies use number for their base and exponent, which is incompatible with BigInt calculations.

    import { dinero, add } from 'dinero.js/bigint';
    import { USD } from 'dinero.js/bigint/currencies';
    
    const d1 = dinero({ amount: 500n, currency: USD });
    const d2 = dinero({ amount: 100n, currency: USD });
    
    const result = add(d1, d2); // a Dinero object with amount `600n`
  6. Define amounts for non-decimal currencies

    For non-decimal currencies, the amount must be expressed in the smallest subdivision. If the currency has multiple subdivisions, the base property of the currency object should be defined as an array. For non-decimal currencies, the currency exponent must be set to 1.
    import { dinero } from 'dinero.js';
    
    // Ancient Greek drachma: 1 drachma = 6 obols
    const GRD = {
      code: 'GRD',
      base: 6,
      exponent: 1,
    };
    const d1 = dinero({ amount: 6, currency: GRD });
    
    // Pre-decimal GBP: 1 pound = 20 shillings, 1 shilling = 12 pence
    const GBP = {
      code: 'GBP',
      base: [20, 12],
      exponent: 1,
    };
    
    // Represents 50 pounds (1,000 shillings or 12,000 pence)
    const d2 = dinero({ amount: 12000, currency: GBP });
  7. Handle the removal of global defaults and settings

    Dinero.js v2 is side-effect free and no longer supports global default settings. All objects must be created with explicit parameters. To simplify object creation, use higher-order functions to partially apply common configurations (like currency).
    import { dinero } from 'dinero.js';
    import { USD } from 'dinero.js/currencies';
    
    // Create a helper for USD objects
    function dineroUSD(amount) {
      return dinero({ amount, currency: USD });
    }
    
    const d = dineroUSD(500);
  8. Multiply Dinero.js amounts by decimals

    You can pass a decimal (e.g., 0.5) to the multiply function. However, because Dinero.js uses integer arithmetic to avoid floating-point precision issues, the operation will throw an error if the resulting amount is not an integer.
    import { dinero, multiply } from 'dinero.js';
    import { USD } from 'dinero.js/currencies';
    
    const d1 = dinero({ amount: 1000, currency: USD }); // $10.00
    multiply(d1, 0.5); // Works (1000 * 0.5 = 500)
    
    const d2 = dinero({ amount: 1001, currency: USD }); // $10.01
    multiply(d2, 0.5); // Throws error (1001 * 0.5 = 500.5)
  9. Format non-decimal currencies with multiple subdivisions

    To handle currencies with multiple subdivisions (e.g., pre-decimalization European currencies or complex fictional currencies), specify the base property of the currency object as an array. Each element in the array represents the conversion rate for that specific subdivision level. Use toUnits with a transformer to format the resulting value array into a human-readable string.
    import { dinero, toUnits } from 'dinero.js';
    
    const POP = {
      code: 'POP',
      base: [30, 16], // 1 donut = 30 cookies, 1 cookie = 16 lollipops
      exponent: 1,
    };
    
    const labels = ['donut', 'cookie', 'lollipop'];
    
    function transformer({ value }) {
      return value
        .filter((amount) => amount > 0)
        .map((amount, index) => `${amount} ${amount > 1 ? `${labels[index]}s` : labels[index]}`)
        .join(' and ');
    }
    
    const d = dinero({ amount: 720, currency: POP });
    
    toUnits(d, transformer); // "1 donut and 15 cookies"
  10. Replace getAmount, getCurrency, and getPrecision with toSnapshot

    In Dinero.js v2, the getAmount, getCurrency, and getPrecision methods have been removed. Use the toSnapshot function instead, which returns a plain object containing the amount, currency, and scale (formerly precision).
    // Before
    const amount = Dinero({ amount: 500, currency: 'USD' }).getAmount();
    const currency = Dinero({ amount: 500, currency: 'USD' }).getCurrency();
    const scale = Dinero({ amount: 500, currency: 'USD' }).getPrecision();
    
    // After
    import { toSnapshot, dinero } from 'dinero.js';
    import { USD } from 'dinero.js/currencies';
    
    const { amount, scale, currency } = toSnapshot(
      dinero({ amount: 500, currency: USD })
    );
  11. Store Dinero objects as JSON snapshots

    For databases supporting JSON columns (e.g., PostgreSQL JSONB, MySQL 5.7+, SQLite JSON1), you can store the entire Dinero snapshot as a single value using toSnapshot. This simplifies the schema but introduces a dependency on JSON support.
    CREATE TABLE products (
      id SERIAL PRIMARY KEY,
      name VARCHAR(255) NOT NULL,
      price JSONB NOT NULL
    );
    import { dinero, toSnapshot } from 'dinero.js';
    import { EUR } from 'dinero.js/currencies';
    
    const price = dinero({ amount: 6999, currency: EUR });
    
    // Insert
    await db.query(
      'INSERT INTO products (name, price) VALUES ($1, $2)',
      ['Mass Effect: Legendary Edition', JSON.stringify(toSnapshot(price))]
    );
    
    // Restore
    const { rows } = await db.query('SELECT * FROM products WHERE id = $1', [1]);
    
    const product = {
      ...rows[0],
      price: dinero(rows[0].price),
    };
  12. Install Dinero.js skills for AI coding agents

    If using an AI coding agent (such as Claude Code, Cursor, or GitHub Copilot), you can install a skills package to provide the agent with best practices and common pitfalls for using Dinero.js.
    npx skills add dinerojs/skills