Getting started · Formatting · Parsing · Diagnostics · Custom codecs · Bundle size
Use parsing when a form or imported field contains localized text such as "$1,234.50" or "1.5 KiB". Import it from @neutrium/formatter/parse; formatting-only applications do not need this import. Use the shared parser for en-US, or createParser({ locale }) for another default locale.
import { parser } from "@neutrium/formatter/parse";
parser.parse("$1,234.50", { kind: "currency", currency: "USD" }); // "1234.5"
parser.parse("12.5%", { kind: "percentage", maximumFractionDigits: 1 }); // "0.125"
parser.parse("1.5 KiB", { kind: "bytes" }); // "1536"
Built-in parsers return canonical strings, including for currencies and durations. They do not return JavaScript numbers. This preserves exact digits and removes presentation padding: "$12.00" becomes "12". Keep that string for exact calculations, or pass it to a Decimal constructor. Converting it with Number() can lose precision.
| Domain | Required specification fields | Parsed result |
|---|---|---|
| Number | kind: "number" |
Exact numeric string; scientific and compact scales are reversed |
| Currency | kind: "currency", currency |
Exact amount in the specified currency |
| Percentage | kind: "percentage" |
Ratio after reversing percentageScale (default 100) |
| Measurement unit | kind: "unit", unit |
Exact value in that unit; no conversion between units |
| Bytes | kind: "bytes" |
Byte count after reversing the SI or IEC suffix |
| Ordinal | kind: "ordinal" |
Numeric string after validating the ordinal pattern |
| Elapsed duration | kind: "duration", presentation: "elapsed" |
Seconds, or milliseconds with inputUnit: "milliseconds" |
| Localized duration | kind: "duration", presentation: "localized" |
Unsupported |
There is no parser for range text or a complete column. Parse each editable value separately. Custom parsers may return other types; see custom codecs.
Use the same locale, precision, signs, and overrides for rendering and parsing. A compiled formatter's .spec preserves those settings, including its effective locale. Reuse it to keep a form's input and display consistent:
import { createFormatter } from "@neutrium/formatter";
import { parser } from "@neutrium/formatter/parse";
const euros = createFormatter({ locale: "de-DE" }).compile({ kind: "currency", currency: "EUR" });
const amountInput = parser.compile(euros.spec);
const text = euros.format("1234.5"); // "1.234,50 €"
amountInput.parse(text); // "1234.5", using the compiled German locale
For uncompiled specifications, supply locale explicitly or configure both instances with the same default locale. Custom codecs that need application context must receive that context on both instances; .spec carries presentation options, not application context.
Parsing recovers only the precision represented by the text. Formatting "1.239" to two fraction digits and parsing "1.24" returns "1.24", not the original input. Sign suppression can also remove information. Retain original domain values separately when the application needs them.
Built-in parsing validates the whole presentation rather than scanning for a number. Grouping, signs, affixes, and precision must agree with the specification. In en-US, "1,234.5" is valid number text but "12,34.5" is not. A currency parser rejects a mismatched currency label.
For an editable field, keep the text while the user types and validate a completed value, for example on blur or submission. Intermediate text such as a lone minus sign is not a complete parseable value.
import { parser } from "@neutrium/formatter/parse";
function validateAmount(text: string): { value: string } | { error: string }
{
try
{
const value = parser.parse(text, { kind: "currency", currency: "USD" });
if (!Number.isFinite(Number(value)))
{
return { error: "Enter a finite amount." };
}
return { value };
}
catch
{
return { error: "Enter a US dollar amount, for example $1,234.50." };
}
}
validateAmount("$12.50"); // { value: "12.5" }
validateAmount("€12.50"); // { error: ... }
Parsing checks presentation syntax. Your form still needs domain rules, such as requiring a finite value or disallowing negative quantities. The example uses Number() only to check finiteness; it returns the original exact string. Keep library error details for diagnostics and give users a message appropriate to the field.
Exact presentation matches are attempted first. Surrounding whitespace is tolerated afterward, but internal whitespace and grouping remain significant. zeroDisplay is recognized as "0" before ordinary numeric syntax, so avoid a placeholder that could also represent a nonzero value. Normal zero text remains parseable alongside the placeholder.
Use supports() to validate a specification, not an input string. parse() still needs error handling for user-entered text, even when supports() returns true.
import { createParser } from "@neutrium/formatter/parse";
const german = createParser({ locale: "de-DE" });
const numbers = german.compile({ kind: "number", maximumFractionDigits: 2 });
["1.234,5", "20,25"].map(numbers.parse); // ["1234.5", "20.25"]
Compile once when several fields use the same input rules. Compilation validates and copies the options, and its parse method works as a callback. Later edits to the original options do not affect it; compile again to change them. Each input still needs error handling. To inspect support, use the original parser's supports() or resolve() methods.
For text displayed at a shared compact scale, compile the series first and give its effective specification to the parser:
import { formatter } from "@neutrium/formatter";
import { parser } from "@neutrium/formatter/parse";
const thousands = formatter.compileSeries([900, 1200], {
kind: "number", notation: "compact", maximumFractionDigits: 1,
});
const input = parser.compile(thousands.spec);
thousands.format(900); // "0.9K"
input.parse("0.9K"); // "900"
thousands.format(2_000_000); // "2,000K" — the selected scale stays fixed
The original automatic specification would normally display 900 without a compact suffix. Its strict parser may therefore reject shared-scale "0.9K". The effective .spec carries the fixed compactExponent needed to accept it. An explicit compactExponent works as well. Byte suffixes carry their own scale and are parseable without this extra step.
import { parser } from "@neutrium/formatter/parse";
const spec = { kind: "duration", presentation: "localized" } as const;
parser.supports(spec); // false
const result = parser.resolve(spec);
if (!result.supported)
{
console.error(`This display cannot be used as an editable duration: ${result.error.message}`);
}
For TypeScript support inspection, assign domain options to a specification variable before passing it to Parser.supports or Parser.resolve; those methods accept the common specification shape. parse and compile infer the complete domain-specific shape directly.
Localized durations cannot be parsed, even when Intl.DurationFormat is available. Use an elapsed duration for reversible scalar text:
import { parser } from "@neutrium/formatter/parse";
parser.parse("1:01:01", {
kind: "duration", presentation: "elapsed", inputUnit: "milliseconds",
}); // "3661000"
See diagnostics and compatibility for runtime feature checks and error handling across both APIs.