Skip to content

Benchmarks

How fast and how small this library is, next to its previous major version and the other libraries on npm that validate Spanish IDs. Every number on this page is read from bench/results/latest.json when the site is built: nothing is typed by hand. If a library is faster or smaller at something, the tables show it.

Where and how the benchmark ran
MachineAMD EPYC 9V74 80-Core Processor
Cores4
Memory16 GiB
Operating systemlinux 6.17.0-1022-azure (x64)
Node.jsv24.21.0
Date2 October 2026
Commit measured1166241
Method3 rounds; each task 2,000 ms after 500 ms of warmup, in its own process; the figure is the median round

Absolute numbers depend on the machine. The ratios between libraries of one run carry over between machines far better, so compare those.

Millions of validations per second (M ops/s, higher is faster), each library called the way its documentation shows, with its default options, on the same valid and invalid documents in canonical form. “This build is” gives how many times as fast as each library this build is, in the same run. unsupported means the library has no validator for that type.

DNI: millions of validations per second (M ops/s, higher is faster) on 225 valid and invalid documents in canonical form, and how many times as fast this build is
LibraryM ops/sThis build is
nif-dni-nie-cif-validation this build60.12—
@maistik/validate-nif 2.0.19.466.4× as fast
dni-js 1.0.08.906.8× as fast
nif-dni-nie-cif-validation 1.0.118.597.0× as fast
better-dni 4.4.28.447.1× as fast
spain-id 1.1.146.968.6× as fast
validator.js isIdentityCard(x, "ES") 13.15.355.7310.5× as fast
validator.js isTaxID(x, "es-ES") 13.15.354.2214.3× as fast
@kreyo/nif-validator 0.1.02.7821.6× as fast
jsvat 2.5.42.1028.6× as fast
stdnum 1.12.60.41146.3× as fast

No other library was faster on the DNI set.

NIE: millions of validations per second (M ops/s, higher is faster) on 150 valid and invalid documents in canonical form, and how many times as fast this build is
LibraryM ops/sThis build is
nif-dni-nie-cif-validation this build57.60—
@maistik/validate-nif 2.0.18.396.9× as fast
better-dni 4.4.28.177.1× as fast
nif-dni-nie-cif-validation 1.0.116.449.0× as fast
dni-js 1.0.04.4912.8× as fast
spain-id 1.1.144.0114.4× as fast
validator.js isTaxID(x, "es-ES") 13.15.353.4216.8× as fast
validator.js isIdentityCard(x, "ES") 13.15.353.1418.4× as fast
@kreyo/nif-validator 0.1.02.6521.7× as fast
jsvat 2.5.41.5936.2× as fast
stdnum 1.12.60.39146.0× as fast

No other library was faster on the NIE set.

CIF: millions of validations per second (M ops/s, higher is faster) on 270 valid and invalid documents in canonical form, and how many times as fast this build is
LibraryM ops/sThis build is
nif-dni-nie-cif-validation this build55.06—
@maistik/validate-nif 2.0.17.747.1× as fast
nif-dni-nie-cif-validation 1.0.117.277.6× as fast
spain-id 1.1.144.5012.2× as fast
@kreyo/nif-validator 0.1.02.5022.0× as fast
jsvat 2.5.42.3723.2× as fast
stdnum 1.12.60.38146.7× as fast
better-dni 4.4.2unsupportedunsupported
dni-js 1.0.0unsupportedunsupported
validator.js isIdentityCard(x, "ES") 13.15.35unsupportedunsupported
validator.js isTaxID(x, "es-ES") 13.15.35unsupportedunsupported

No other library was faster on the CIF set.

Speed is not everything, and these figures leave things out: memory, the first call before the JIT warms up, input that needs normalization, and the cost of validate()’s detailed result. A library that rejects a string early is fast on it, which is why the agreement with SPEC.md is measured separately, below.

What one validator adds to a bundle, minified and gzipped (min+gzip, lower is smaller), bundled with esbuild as the library’s own size check does. The bar of a library much larger than the others is drawn full, with a broken end.

Size of one validator of any type (min+gzip, lower is smaller)
LibraryAny type
@maistik/validate-nif 2.0.1456 B
@kreyo/nif-validator 0.1.0533 B
spain-id 1.1.14583 B
dni-js 1.0.0882 B
nif-dni-nie-cif-validation this build929 B
better-dni 4.4.21,134 B
jsvat 2.5.41,186 B
nif-dni-nie-cif-validation 1.0.111,517 B
validator.js isIdentityCard(x, "ES") 13.15.352,539 B
validator.js isTaxID(x, "es-ES") 13.15.356,537 B
stdnum 1.12.651,063 B

Smaller than this build for one validator of any type: @maistik/validate-nif 2.0.1 (456 B), @kreyo/nif-validator 0.1.0 (533 B), spain-id 1.1.14 (583 B) and dni-js 1.0.0 (882 B). Also measured: stdnum 1.12.6 with a deep import of its Spanish NIF module (not documented), 2,468 B.

Some smaller libraries validate fewer types. This one spends bytes on input normalization, the CIF-3 control types and K/L/M NIFs. Libraries published only as CommonJS can’t be tree-shaken, so one of their functions costs the whole library. Multi-country and multi-purpose libraries, such as stdnum and validator.js, are larger by design.

Size of one validator of each type and of the whole library (min+gzip, lower is smaller)
LibraryDNINIECIFAny typeWhole library
@maistik/validate-nif 2.0.1215 B243 B319 B456 B1,189 B
@kreyo/nif-validator 0.1.0533 B533 B533 B533 B746 B
spain-id 1.1.14165 B237 B363 B583 B704 B
dni-js 1.0.0883 B882 Bunsupported882 B880 B
nif-dni-nie-cif-validation this build623 B586 B568 B929 B4,679 B
better-dni 4.4.21,135 B1,135 Bunsupported1,134 B1,132 B
jsvat 2.5.41,186 B1,186 B1,186 B1,186 B5,266 B
nif-dni-nie-cif-validation 1.0.111,517 B1,517 B1,518 B1,517 B1,514 B
validator.js isIdentityCard(x, "ES") 13.15.352,539 B2,539 Bunsupported2,539 B44,449 B
validator.js isTaxID(x, "es-ES") 13.15.356,537 B6,537 Bunsupported6,537 B44,449 B
stdnum 1.12.651,063 B51,063 B51,063 B51,063 B51,054 B

The share of the fixtures of the library’s test suite (default options) that each library judges as SPEC.md does: valid or invalid.

Agreement with SPEC.md (not correctness in the absolute): the share of the fixtures each library judges as SPEC.md does, on all of them and on canonical input only
LibraryAll fixturesCanonical input only
nif-dni-nie-cif-validation this build100.0%100.0%
nif-dni-nie-cif-validation 1.0.1184.0%92.2%
spain-id 1.1.1482.4%85.4%
better-dni 4.4.271.9%86.7%
dni-js 1.0.073.4%86.7%
stdnum 1.12.676.8%77.7%
validator.js isIdentityCard(x, "ES") 13.15.3575.0%86.7%
validator.js isTaxID(x, "es-ES") 13.15.3584.4%100.0%
@maistik/validate-nif 2.0.182.4%86.4%
@kreyo/nif-validator 0.1.082.4%86.4%
jsvat 2.5.486.4%89.3%
Agreement with SPEC.md per document type, and the disagreements on decisions that SPEC.md documents
LibraryDNINIECIFK/L/MDisagreementsOn a documented decision
nif-dni-nie-cif-validation this build100.0%100.0%100.0%100.0%00
nif-dni-nie-cif-validation 1.0.1173.3%88.9%83.6%100.0%2013
spain-id 1.1.1490.0%77.8%86.9%36.4%2213
better-dni 4.4.273.3%77.8%unsupported45.5%187
dni-js 1.0.076.7%77.8%unsupported45.5%177
stdnum 1.12.690.0%83.3%63.9%100.0%2929
validator.js isIdentityCard(x, "ES") 13.15.3580.0%77.8%unsupported45.5%166
validator.js isTaxID(x, "es-ES") 13.15.3580.0%77.8%unsupported100.0%105
@maistik/validate-nif 2.0.186.7%77.8%86.9%45.5%2214
@kreyo/nif-validator 0.1.086.7%77.8%86.9%45.5%2214
jsvat 2.5.490.0%83.3%82.0%100.0%1717

A type that a library doesn’t validate is left out of its figures, so compare the columns rather than “All fixtures” across libraries of a different scope. The list of disagreements of each library, with an example of each, is in latest.md.

  • Inputs. Generated from a seeded pseudo-random generator, so every run and every machine measures the same strings: valid documents and the same documents with a wrong control character, for each type.
  • Calls. Each library is called exactly as its documentation shows, with its default options, on the raw input string. The one exception is jsvat, a VAT validator, whose inputs get an ES prefix, added outside the timed loop.
  • Timing. tinybench runs each task in its own process, after a warmup, so what V8 learns from one library can’t change the next one’s figure. The whole run repeats in rounds, the order of the libraries rotates between sets and rounds, and each figure is its median round.
  • Size. The single-function import that each library documents, bundled and minified with esbuild and gzipped at level 9, minus the cost of an empty import.
  • Agreement. Every library’s “any type” call on the fixtures that test the default options, each fixture judged valid or invalid.

The full methodology, its caveats and the schema of the results are in bench/README.md.

The libraries are installed at exact versions, so a run measures exactly what latest.json lists. Close other programs first, and compare ratios rather than absolute numbers:

Terminal window
git clone https://github.com/josegoval/nif-dni-nie-cif-validation.git
cd nif-dni-nie-cif-validation
pnpm install --frozen-lockfile
pnpm bench:competitors # about 5 minutes; writes bench/results/latest.json and latest.md

pnpm bench compares every validator with version 1 instead. A GitHub Actions workflow runs the same benchmark on demand, on a shared runner, and uploads the results.