Hooks

useNumberFormat

Provider-agnostic number formatting context for Docyrus UI components. Mirror of useDateFormat — wired automatically by DocyrusTenantProvider, but works with any number formatting library.

Installation

pnpm dlx @docyrus/cli add @docyrus/hooks-use-number-format

The module exports useNumberFormat() (hook) and <NumberFormatProvider> (provider). It has zero runtime dependencies — the formatter itself is supplied by you (or by a parent <DocyrusTenantProvider>).

Overview

useNumberFormat() is the read side of a small provider-agnostic context. Components inside the tree call it to get a formatNumber(value, options?) function; the provider above the tree decides how the formatting actually happens.

  • No provider mounted → the hook returns a built-in sentinel that renders String(value). Data-grid / data-table hooks detect this and skip injecting the formatter so the cell's own locale-aware fallback (Intl.NumberFormat for currency / percent) still runs.
  • <DocyrusTenantProvider> mounted → the provider installs a formatNumber backed by the tenant's numberUtils (from @docyrus/app-utils), so every cell / chart axis / value renderer respects the tenant's decimalSeparator, thousandSeparator, decimalPrecision, and locale settings.
  • Custom provider mounted → you decide. Wrap any library (Intl, dinero.js, your own helper) with <NumberFormatProvider> and components inside the tree pick it up the same way.

Usage

Consume in a component

import { useNumberFormat } from '@docyrus/ui/hooks/use-number-format';

function Total({ amount, currency }: { amount: number; currency: string }) {
  const { formatNumber } = useNumberFormat();

  return <span>{formatNumber(amount, { variant: 'currency', currency })}</span>;
}

Standard setup — let <DocyrusTenantProvider> do it

If your app already wraps its root in <DocyrusTenantProvider>, you don't have to do anything. Tenant-aware formatNumber is already wired.

Custom provider (Intl, dinero.js, …)

import { NumberFormatProvider } from '@docyrus/ui/hooks/use-number-format';

function App({ children }) {
  return (
    <NumberFormatProvider
      formatNumber={(value, opts) => {
        const variant = opts?.variant ?? 'number';
        const num = Number(value);
        if (Number.isNaN(num)) return String(value ?? '');
        return new Intl.NumberFormat('en-US', {
          style: variant === 'currency' ? 'currency' : variant === 'percent' ? 'percent' : 'decimal',
          currency: opts?.currency
        }).format(variant === 'percent' ? num : num);
      }}>
      {children}
    </NumberFormatProvider>
  );
}

API Reference

useNumberFormat()

Returns NumberFormatContextValue.

FieldTypeDescription
formatNumber(value: unknown, options?: NumberFormatOptions) => stringFormats a number using the active provider's strategy.

NumberFormatOptions

FieldTypeDescription
variant'number' | 'currency' | 'percent'Output style. Default 'number'. 'percent' multiplies the input by 100 and appends %.
currencystringISO 4217 currency code, used when variant === 'currency'.
decimalPrecisionnumberOverride the locale's default fraction digits. Pass 0 for identifier-like numerics.
thousandSeparatorstringOverride the locale's default thousands grouping. Empty string disables grouping.

<NumberFormatProvider>

PropTypeDescription
formatNumberNumberFormatFnRequired. The implementation that backs useNumberFormat() inside the tree.
childrenReactNodeTree to wrap.

isDefaultNumberFormatContext(ctx)

Returns true when the value returned by useNumberFormat() is the built-in no-provider sentinel (raw String(value) renderer) rather than a real provider. Internal hooks use this to avoid shadowing a cell's locale-aware fallback when no provider is mounted. Most apps don't need to call it directly.

Type Exports

TypeDescription
NumberFormatFnSignature of the formatter function.
NumberFormatOptionsOptions accepted by formatNumber.
NumberFormatContextValueShape returned by useNumberFormat().

On this page