Parse numeric values with units such as 12px, 50 gold, -1.5turn, 100%, or 0 into structured results with readable diagnostics.
numeric-unit-parse-kit is a small clean-room toolkit for forms, config editors, import tools and design-token utilities that need to validate a single number plus an optional unit. It is deliberately narrower than a CSS parser, expression parser or unit converter.
- TypeScript types are generated from the source.
- ESM-only package with no runtime dependencies.
- Marked as side-effect free for bundlers.
- Browser-friendly implementation with no Node-only APIs.
- CI runs
npm ci,typecheck,build, andtest. - Tested on Node.js 20 and 22 with GitHub Actions.
npm install numeric-unit-parse-kitimport {
createNumericUnitParser,
formatNumericUnit,
isNumericUnit,
parseNumericUnit
} from "numeric-unit-parse-kit";
const parsed = parseNumericUnit(" 50 gold ", {
allowedUnits: ["gold", "silver"],
requireUnit: true
});
if (parsed.ok) {
parsed.value.amount;
// 50
parsed.value.unit;
// "gold"
parsed.value.normalized;
// "50gold"
}
isNumericUnit("12px", { allowedUnits: ["px", "rem"] });
// true
formatNumericUnit({ amount: 12, unit: "gold" }, { separator: " " });
// "12 gold"
const cssLength = createNumericUnitParser({
allowedUnits: ["px", "rem", "%"],
requireUnit: true
});
cssLength.isValid("1.5rem");
// trueSmall numeric-unit inputs show up in many places: design tokens, spacing controls, game configs, quota settings, billing quantities, virtual currencies, custom measurements and import columns.
Boolean validation is often not enough. This package returns stable issue codes and keeps the parsed value small:
const result = parseNumericUnit("12bananas", {
allowedUnits: ["px", "rem"]
});
result.issues;
// [
// {
// code: "unit-not-allowed",
// message: "Unit \"bananas\" is not in the allowed unit list.",
// input: "12bananas",
// normalizedInput: "12bananas",
// unit: "bananas"
// }
// ]Returns a discriminated result:
type ParseNumericUnitResult =
| { ok: true; value: NumericUnit; issues: [] }
| { ok: false; value: null; issues: NumericUnitIssue[] };
type NumericUnit = {
amount: number;
unit: string;
raw: string;
normalized: string;
};Example:
const result = parseNumericUnit("-1.5turn", {
allowedUnits: ["deg", "rad", "turn"],
allowNegative: true
});Boolean shortcut for parseNumericUnit(input, options).ok.
isNumericUnit("100%", { allowPercent: true });
// trueFormats { amount, unit } back to a string.
formatNumericUnit({ amount: 1.2345, unit: "px" }, { maximumFractionDigits: 2 });
// "1.23px"
formatNumericUnit({ amount: 12, unit: "gold" }, { separator: " " });
// "12 gold"By default, formatNumericUnit({ amount: 0, unit: "px" }) returns "0". Pass { unitlessZero: false } when the unit should be preserved.
Creates a small parser object that reuses the same defaults across a form, importer or config validator. Per-call options override the defaults.
const cssLength = createNumericUnitParser({
allowedUnits: ["px", "rem"],
allowPercent: false,
requireUnit: true
});
cssLength.parse("12px");
cssLength.isValid("12rem");
cssLength.format({ amount: 12, unit: "px" });
cssLength.isValid("50%", {
allowPercent: true,
allowedUnits: ["px", "%"]
});
// true| Option | Default | Description |
|---|---|---|
allowedUnits |
none | Optional list of accepted units. Empty unit values are still allowed unless requireUnit rejects them. |
caseSensitiveUnits |
true |
Compare allowedUnits using exact casing by default. |
requireUnit |
false |
Reject values without units. |
allowUnitlessZero |
true |
Allow 0 when requireUnit is enabled. |
allowNegative |
true |
Allow negative numeric amounts. |
allowPercent |
true |
Allow % as a unit. |
trim |
true |
Trim surrounding whitespace before parsing. |
| Option | Default | Description |
|---|---|---|
unitlessZero |
true |
Format zero as 0 even when a unit is present. |
maximumFractionDigits |
none | Round the amount with Intl.NumberFormat. |
separator |
"" |
String placed between amount and unit. |
Issue codes are stable and intended for UI messages, logs or localization:
emptyinvalid-typeinvalid-syntaxnon-finitenegative-not-allowedunit-requiredunit-not-allowedpercent-not-allowed
This package parses one numeric value and one unit. It does not:
- parse formulas such as
calc(100% - 1rem); - parse ranges such as
1-3px; - parse compound dimensions such as
10px 20px; - convert between units;
- validate CSS grammar;
- infer whether a unit makes sense for a domain.
Use it as a small validation layer before domain-specific checks.
MPL-2.0