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:
| Method | Description |
|---|---|
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;
// 123456Returned API:
| Method | Description |
|---|---|
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);
// 5Result 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 + 1isIntermediate 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.