Validate Spanish NIF, DNI, NIE and CIF numbers
Correct, fast, small and typed. Every rule cites its source in SPEC.md, and every error says why.
npm i nif-dni-nie-cif-validation yarn add nif-dni-nie-cif-validation pnpm add nif-dni-nie-cif-validation bun add nif-dni-nie-cif-validationAs of 3 October 2026
Try it
Type or paste a NIF, DNI, NIE or CIF. The library itself checks it as you type, and tells you why it is wrong.
Spaces, dots, hyphens and lower case are fine, as in 12.345.678-z.
The result appears here as you type.
The live validator needs JavaScript. The rest of this page works without it.
Runs entirely in your browser: no data leaves your browser.
Generated numbers are made up and only for tests: one may match a real person or company by chance.
Why this library
Correct, with sources
24 rules, each with an ID (DNI-2, NIE-3, CIF-3…) and its documented basis in SPEC.md: an official source (the BOE, the AEAT), a convention labelled as such, or the library's contract. Every error names the rule that failed.
Fast
At least 6.3× the throughput of every other library tested
60.1 M ops/s on DNI (millions of validations per second, higher is faster), and 7.0× to 8.9× the throughput of v1.
Small
929 B min+gzip for isValidNif, 4.7 kB for the whole library. Tree-shakable, and each language is its own import.
Some single-purpose libraries are smaller for one validator (@maistik/validate-nif 456 B, @kreyo/nif-validator 533 B, spain-id 583 B and dni-js 882 B). Some of them validate fewer types: this one spends bytes on input normalization, the CIF-3 control types and K/L/M support.
0 dependencies
No runtime dependencies. Zod, Valibot and Yup are optional peer dependencies, needed only by their adapters.
Typed
Written in TypeScript, with declarations for import and require. ES modules and CommonJS, compiled to ES2016.
Never throws
Validators take unknown: null, numbers and objects give false, or NOT_A_STRING from validate(). Never a crash on user input.
5 languages
Error messages and organisation names in English, Spanish, Catalan, Basque and Galician. The validator above speaks the language of this page.
Generators and schemas
Seeded test-data generators, and Zod, Valibot and Yup schemas that output the normalized value and the localized message.
Measured on AMD EPYC 9V74 80-Core Processor with Node.js v24.21.0 on 2 October 2026, each library with its default options. Ratios carry over between machines better than absolute numbers.
Use it
Boolean checks for a yes or no, validate() when you need to know why, opt-in entry points for schemas and test data, and a command line for terminals, scripts and AI agents.
import { isValidNif } from "nif-dni-nie-cif-validation";
isValidNif("12345678Z"); // trueisValidNif(" 12.345.678-z "); // true: spaces, dots and hyphens are removed, and lower case becomes upper caseisValidNif("B12345675"); // false: wrong control digitisValidNif(null); // false: never throwsimport { validate } from "nif-dni-nie-cif-validation";
validate(" b-1234567-4 ");// { valid: true, type: "CIF", normalized: "B12345674",// meta: { orgKey: "B", orgDescription: "Limited liability company" } }
validate("12345678A").error;// { code: "INVALID_CONTROL_CHARACTER", rule: "DNI-2", expected: "Z",// message: 'The control character is not correct: for this DNI it should be "Z".' }
validate(" 12.345.678-z ").normalized; // "12345678Z": store thisimport { z } from "zod";import { zNif } from "nif-dni-nie-cif-validation/zod";
const schema = z.object({ nif: zNif({ types: ["DNI", "NIE"] }) });
schema.parse({ nif: " 12.345.678-z " }); // { nif: "12345678Z" }schema.safeParse({ nif: "B12345674" }).success; // false: a CIF is not accepted hereimport * as v from "valibot";import { vNif } from "nif-dni-nie-cif-validation/valibot";
const schema = v.object({ nif: vNif({ types: ["DNI", "NIE"] }) });
v.parse(schema, { nif: " 12.345.678-z " }); // { nif: "12345678Z" }v.safeParse(schema, { nif: "B12345674" }).success; // false: a CIF is not accepted hereimport { object } from "yup";import { yNif } from "nif-dni-nie-cif-validation/yup";
const schema = object({ nif: yNif({ types: ["DNI", "NIE"] }) });
schema.validateSync({ nif: " 12.345.678-z " }); // { nif: "12345678Z" }schema.isValidSync({ nif: "B12345674" }); // false: a CIF is not accepted hereimport { createGenerator, generateCif, generateDni, generateNie } from "nif-dni-nie-cif-validation/generate";
generateDni({ seed: 1 }); // "62707394X": the same on every run and platformgenerateNie({ seed: 1, prefix: "Z" }); // "Z6270739R"generateCif({ seed: 1, orgKey: "B" }); // "B62707393"generateDni({ seed: 1, format: true }); // "62707394-X"
const gen = createGenerator(42); // a seeded stream: different values, the same on every rungen.nif(); // "Y8524657C"gen.nif(); // "X5265925Y"npx nif-dni-nie-cif-validation validate 12345678Z B12345674 12345678A# 12345678Z: valid DNI 12345678Z# B12345674: valid CIF B12345674 (Limited liability company)# 12345678A: invalid [DNI-2 INVALID_CONTROL_CHARACTER] The control character is not correct: for this DNI it should be "Z".npx nif-dni-nie-cif-validation generate dni --count 2 --seed 42# 60110375J# 44829056Dnpx downloads the package the first time. JSON output for scripts and agents, exit codes, test data and CSV checks are in the command line guide.
How it compares
Throughput on the DNI set, in millions of validations per second (M ops/s, higher is faster), and the size of one validator of any type (min+gzip, lower is smaller).
| Library | DNI, M ops/s | Size, any type |
|---|---|---|
| nif-dni-nie-cif-validation this build | 929 B | |
| @maistik/validate-nif 2.0.1 | 456 B | |
| dni-js 1.0.0 | 882 B | |
| nif-dni-nie-cif-validation 1.0.11 | 1,517 B | |
| better-dni 4.4.2 | 1,134 B | |
| spain-id 1.1.14 | 583 B | |
| validator.js isIdentityCard(x, "ES") 13.15.35 | 2,539 B | |
| validator.js isTaxID(x, "es-ES") 13.15.35 | 6,537 B | |
| @kreyo/nif-validator 0.1.0 | 533 B | |
| jsvat (Spain) 2.5.4 | 1,186 B | |
| stdnum 1.12.6 | 51,063 B |
Every number comes from bench/results/latest.json. Speed is not everything: the benchmarks page links to the full results, which also compare agreement with SPEC.md and features.