Raqam
API Reference

Core Utilities

Low-level formatting, parsing, locale registration, and caret helpers from raqam/core and raqam/server.

raqam/core exposes the formatter/parser engine underneath the React APIs. raqam/server is an alias of the same entrypoint, useful when you want to make the server-safe intent explicit in RSC, SSR, or Edge code.

Import

import {
  createFormatter,
  createParser,
  normalizeDigits,
  registerLocale,
  getCaretBoundary,
  computeNewCursorPosition,
  presets,
} from "raqam/core";

// Equivalent server-focused import:
import { createFormatter } from "raqam/server";

createFormatter(options)

Build a cached Intl.NumberFormat wrapper with optional affixes and fraction digit overrides.

const formatter = createFormatter({
  locale: "en-US",
  formatOptions: { style: "currency", currency: "USD" },
  maximumFractionDigits: 2,
  fixedDecimalScale: true,
});

formatter.format(1234.5);
// "$1,234.50"

Returned API:

MethodDescription
format(value)Returns the formatted string.
formatToParts(value)Returns Intl.NumberFormatPart[].
formatResult(value)Returns { formatted, parts }.
getLocaleInfo()Returns separators, minus sign, zero digit, and RTL info.

createParser(options)

Create a locale-aware parser that accepts the same locale, affix, and sign/decimal constraints used by the formatter.

const parser = createParser({
  locale: "fa-IR",
  suffix: " تومان",
});

parser.parse("۱۲۳٬۴۵۶ تومان").value;
// 123456

Returned API:

MethodDescription
parse(input)Returns { value, isValid, isIntermediate }.
isIntermediate(input)Detects incomplete-but-valid editing states like "-" or "1.".
getLocaleInfo()Returns locale metadata used by the parser.
strip(input)Strips formatting affordances (grouping separators, currency symbol, prefix/suffix, percent sign) from input, returning the bare numeric string (ASCII digits, optional leading -, at most one .). Typed trailing zeros are preserved. Returns string.

normalizeDigits(input)

Converts registered non-Latin digits to ASCII without touching other characters.

normalizeDigits("۱2٣");
// "123"

Use this when you need normalized digits before your own validation or storage logic.

registerLocale(config)

Add support for another digit block.

// Gujarati digits ૦–૯ (U+0AE6–U+0AEF) — not a built-in block.
registerLocale({
  digitBlocks: [[0x0ae6, 0x0aef]],
});

normalizeDigits("૧૨૩");
// "123"

Use this only for scripts not already covered by the built-ins. The built-in blocks are Arabic-Indic, Extended Arabic-Indic (Persian), Devanagari, Bengali, Thai, and fullwidth (U+FF10–U+FF19, emitted by full-width CJK IMEs) — these are normalized out of the box, no registerLocale needed. For Persian, Arabic, Hindi, Bengali, and Thai, prefer the ready-made modules in raqam/locales/*.

Caret helpers

These power the live-formatting cursor behavior and are useful only when you build your own custom input pipeline.

getCaretBoundary(formattedValue, localeInfo)

Returns a boolean[] showing which positions in a formatted string are valid caret stops.

computeNewCursorPosition(oldInput, oldCursor, newFormatted, localeInfo, inputType?)

Maps the caret from the pre-format string to the new formatted string after an edit. The optional inputType (the native InputEvent.inputType, e.g. "deleteContentBackward") refines deletion handling around grouping separators.

const formatter = createFormatter({ locale: "en-US" });
const info = formatter.getLocaleInfo();

computeNewCursorPosition("1234", 4, "1,234", info);
// 5

Result types

The formatter and parser return these shapes (all exported as types from raqam, raqam/core, and raqam/server):

interface LocaleInfo {
  decimalSeparator: string;   // "." en-US, "," de-DE, "٫" fa-IR
  groupingSeparator: string;  // "," en-US, "." de-DE, "٬" fa-IR
  minusSign: string;          // usually "-", but locale-specific
  zero: string;               // "0" Latin, "۰" Persian, …
  isRTL: boolean;             // true for ar/he/fa/ur/…
}

interface ParseResult {
  value: number | null;       // parsed number, or null if empty/invalid
  isValid: boolean;           // true for a complete, valid number
  isIntermediate: boolean;    // valid-but-incomplete ("1.", "1.50", "-")
}

interface FormatResult {
  formatted: string;          // the full formatted string
  parts: Intl.NumberFormatPart[];
}

type CaretBoundary = boolean[]; // length = formatted.length + 1

isIntermediate is what powers live formatting — see Formatting & Behavior → Intermediate states.

Presets from the core entrypoint

presets is also available from raqam/core and raqam/server, so you can share formatting options between client and server code.

import { createFormatter, presets } from "raqam/server";

const formatter = createFormatter({
  locale: "en-US",
  formatOptions: presets.currency("USD"),
});

Reach for these APIs when you need formatting or parsing without the React state machine. For actual inputs, prefer useNumberFieldState + useNumberField or the NumberField components.

On this page