1001Ferramentas
๐Ÿ†”Validators

CPF/CNPJ Detector & Validator

Auto-detect if a string is CPF (11 digits) or CNPJ (14 digits) and validate its check digit.

โ€”

Auto-detect CPF or CNPJ from a single input: the modern Brazilian form pattern

A pervasive pain point in Brazilian e-commerce, fintech and onboarding flows is the "CPF or CNPJ?" choice. Forcing the user to select a radio button before typing adds friction, raises bounce rates and is inconsistent with how the document holders actually think about themselves. The modern convention, adopted by Stripe BR Checkout, Mercado Pago, RD Station, Hotmart and most large Brazilian SaaS, is to expose a single field labelled "CPF ou CNPJ" that auto-detects the document type from what the user types.

The technique relies on a hard structural difference: CPF has 11 digits, CNPJ has 14 digits. Strip all non-numeric characters from the input, count the digits, then route to the corresponding check-digit algorithm. Both share the modulo 11 family of algorithms but use different weights and lengths, so detection must happen before validation.

Reference detection routine

function detectDoc(raw) {
  const digits = String(raw).replace(/\D/g, '')
  if (digits.length === 11) return { type: 'CPF', value: digits }
  if (digits.length === 14) return { type: 'CNPJ', value: digits }
  return { type: 'UNKNOWN', value: digits }
}

The same routine should run on every oninput event so the UI can show a dynamic mask XXX.XXX.XXX-XX for CPF or XX.XXX.XXX/XXXX-XX for CNPJ as soon as the 11th or 14th digit lands. Libraries that implement this well: imask.js (vanilla), react-input-mask and vue-the-mask. For React Hook Form, you can compose a Zod resolver that branches by length.

Length plus format: detection done right

Length alone is enough for traditional CPF and CNPJ, but only after stripping the mask. A naive value.length check on "123.456.789-09" returns 14 (the length of a raw CNPJ) and breaks detection. Always normalize first:

  • Strip ., -, /, spaces and Unicode whitespace.
  • Reject inputs that contain anything other than digits (or the alphanumeric CNPJ alphabet).
  • Branch on length: 11 -> CPF, 14 -> CNPJ, anything else -> show "incomplete" hint.
  • Run the matching check-digit algorithm only after a length match.

Alphanumeric CNPJ (July 2026): a detection gotcha

Starting July 2026, the Receita Federal issues alphanumeric CNPJs: the first 12 positions accept letters A-Z and digits 0-9, the last two remain numeric check digits computed via the same modulo 11 algorithm but with the ASCII value of each character. Auto-detect routines must accept both old and new formats:

  • Normalize letters to uppercase before validating.
  • Allow A-Z 0-9 for the first 12 chars of a CNPJ-length input; reject lowercase or non-ASCII letters silently.
  • CPF stays numeric-only โ€” any letter in an 11-char input is a sure sign of typo.
  • Mask renders the same: XX.XXX.XXX/XXXX-XX, just with letters allowed in the first 12 slots.

Anti-patterns to avoid

  • Two separate fields: confuses freelancers (MEIs) who own both numbers and increases conversion drop-off by 5-12 percent in A/B tests.
  • Format-by-format regex without length normalization: matches the masked length and miscounts digits.
  • Validation without type discrimination: a single validate(doc) that internally tries CPF then CNPJ leaks the type information back via timing and error messages โ€” confusing for users.
  • Trusting client-side validation: always re-validate server-side. A determined user can bypass any browser-side check.

TypeScript discriminated union

type Doc =
  | { type: 'CPF'; value: string }
  | { type: 'CNPJ'; value: string }
  | { type: 'INVALID'; reason: string }

function parseDoc(raw: string): Doc { /* ... */ }

Discriminated unions let the compiler narrow downstream code (issuing receipts, calling RFB APIs, choosing rate-limit buckets) without unsafe casts. Pair with the cpf-cnpj-validator npm package, which exposes cpf.isValid, cnpj.isValid, cpf.format and cnpj.format, all working on raw digits.

FAQ

Can I really detect the type just from length?

Yes. After stripping non-digit characters, 11 means CPF and 14 means CNPJ. No other Brazilian document shares those lengths, so the heuristic is unambiguous.

How do I detect the new alphanumeric CNPJ?

Allow letters A-Z in positions 1-12 of any 14-character input. The presence of any letter is itself a strong CNPJ signal, since CPF remains numeric-only.

Is there a battle-tested JS library?

Yes: cpf-cnpj-validator (npm, ~250 KB weekly downloads). It handles formatting, stripping and check-digit math for both documents. For server side, brazilian-values in Go and brutils in PHP are equivalents.

What if the user pastes with extra characters?

Normalize before validating: strip dots, slashes, dashes and whitespace, then count digits. Trim emojis and zero-width spaces too โ€” they sneak in from clipboard apps.

Should I validate client-side or server-side?

Both. Client-side validation provides instant feedback and reduces bad submissions. Server-side validation is the only one you can trust for business logic, fraud rules and persistence.

Related Tools