DEV Community

Cover image for I built a zero-dependency TypeScript library to validate Spanish DNI, NIE and CIF
Pol Gubau Amores
Pol Gubau Amores

Posted on

I built a zero-dependency TypeScript library to validate Spanish DNI, NIE and CIF

If you've ever built a form in Spain, you know the pain: validating a DNI is straightforward, but then someone enters a NIE, or a company CIF, and suddenly you're copy-pasting regex from a 2009 Stack Overflow answer.

I got tired of that. So I built @polgubau/validar-dni — a tiny, typed, zero-dependency library that handles all three.

What it does

import { validDniCifNie, parseDni } from "@polgubau/validar-dni";

// Simple boolean check
validDniCifNie("12345678Z"); // true
validDniCifNie("12345678A"); // false

// Rich result — tells you what's wrong
parseDni("12345678A");
// {
//   isValid: false,
//   type: "NIF",
//   normalized: "12345678A",
//   expectedControl: "Z"  ← the correct letter
// }
Enter fullscreen mode Exit fullscreen mode

It covers:

Type Format Example
NIF (DNI) 8 digits + letter 12345678Z
NIE X/Y/Z + 7 digits + letter X1234567L
CIF Org letter + 7 digits + letter/digit A58818501
NIE especial T + 8 digits T12345678

Why not just use a regex?

A regex can check the format. But Spanish identifiers have a control character — a letter or digit derived from the rest of the number using a specific algorithm. A regex can't validate that.

parseDni always returns the expectedControl, even when the input is invalid. So instead of just saying "wrong", you can tell the user "the correct letter is Z".

Architecture

The library uses a Strategy pattern — each document type is an isolated validator implementing a Validator interface:

interface Validator {
  matches(normalized: string): boolean;
  validate(normalized: string): DniResult;
}
Enter fullscreen mode Exit fullscreen mode

Adding a new document type means creating one file and adding it to the array. Zero changes to existing code.

Tree-shaking

If you only need NIF validation, import just that:

import { nifValidator } from "@polgubau/validar-dni/nif"; // ~115 bytes
Enter fullscreen mode Exit fullscreen mode

vs the full bundle at ~1 KB.

Stats

  • Zero runtime dependencies
  • 100% branch coverage
  • ~1 KB full bundle, ~115 bytes per subpath
  • ESM + CJS, full .d.ts types
  • Works in Node, browsers, React, Next.js, Deno

Install

npm install @polgubau/validar-dni
Enter fullscreen mode Exit fullscreen mode

GitHub: github.com/PolGubau/validar-dni


Built with TypeScript, bundled with tsup, linted with Biome. MIT licensed.

Would love feedback, especially if there are edge cases I'm missing.

To know more details check https://www.polgubau.com/

Top comments (0)