IBAN Validator — JavaScript source
Validate International Bank Account Numbers (IBAN) with the mod-97 checksum, verify the country-specific length, and format the result. 100% client-side, no network.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
/**
* Pure IBAN validation logic - ISO 13616 mod-97 checksum.
*
* Language: JavaScript (ES module)
* CosmoDev polyglot showcase port of the `iban-validator` tool.
* Ported from src/lib/iban.ts - display source, part of CosmoDev's
* polyglot tool pages.
*
* Normalizes the input (uppercase, strip spaces/dashes), checks the
* structural regex (country code + 2 check digits + 1-30 BBAN chars),
* verifies the per-country length, then runs the ISO 13616 mod-97
* checksum: move the first 4 chars to the end, map A=10..Z=35, and
* confirm the resulting integer is congruent to 1 mod 97. BigInt (built
* into the language) keeps the arithmetic exact for any length. Never
* throws - always returns an info object.
*/
// Per-country IBAN lengths (ISO 13616) - a representative subset.
export const IBAN_LENGTHS = {
AL: 28, AD: 24, AT: 20, AZ: 28, BH: 22, BY: 28, BE: 16, BA: 20, BR: 29,
BG: 22, CR: 22, HR: 21, CY: 28, CZ: 24, DK: 18, DO: 28, EE: 20, FO: 18,
FI: 18, FR: 27, GE: 22, DE: 22, GI: 23, GR: 27, GL: 18, GT: 28, HU: 28,
IS: 26, IE: 22, IL: 23, IT: 27, JO: 30, KZ: 20, XK: 20, KW: 30, LV: 21,
LB: 28, LI: 21, LT: 20, LU: 20, MK: 19, MT: 31, MR: 27, MU: 30, MC: 27,
MD: 24, ME: 22, NL: 18, NO: 15, PK: 24, PS: 29, PL: 28, PT: 25, QA: 29,
RO: 24, LC: 32, SM: 27, ST: 25, SA: 24, RS: 22, SC: 31, SK: 24, SI: 19,
SG: 19, ES: 24, SE: 24, CH: 21, TL: 23, TN: 24, TR: 26, UA: 29, AE: 23,
GB: 22, VG: 24,
};
/** mod-97 checksum over a CLEANED iban (no spaces, uppercase). */
export function mod97Check(cleaned) {
// Move the first 4 chars (country + check) to the end.
const rearranged = cleaned.slice(4) + cleaned.slice(0, 4);
let numeric = '';
for (const ch of rearranged) {
const code = ch.charCodeAt(0);
if (code >= 48 && code <= 57) {
numeric += ch;
} else if (code >= 65 && code <= 90) {
numeric += String(code - 55); // A=10 .. Z=35
} else {
return false; // invalid character
}
}
// Fold mod 97 one decimal digit at a time (BigInt mirrors the TS lib).
let rem = 0n;
for (const ch of numeric) {
rem = (rem * 10n + BigInt(ch.charCodeAt(0) - 48)) % 97n;
}
return rem === 1n;
}
/** Validate an IBAN. Always returns an info object; never throws. */
export function validateIban(input) {
const cleaned = String(input || '').toUpperCase().replace(/[\s-]/g, '');
const cc = /^[A-Z]{2}/.test(cleaned) ? cleaned.slice(0, 2) : null;
const info = {
input: input ?? '',
cleaned,
countryCode: cc,
valid: false,
checksumOk: false,
lengthOk: false,
expectedLength: cc ? IBAN_LENGTHS[cc] ?? null : null,
formatted: cleaned.replace(/(.{4})(?=.)/g, '$1 ').trim(),
error: null,
};
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/.test(cleaned)) {
return { ...info, error: 'Invalid IBAN format.' };
}
const lengthOk = info.expectedLength === null ? true : cleaned.length === info.expectedLength;
const checksumOk = mod97Check(cleaned);
if (!lengthOk) {
return { ...info, lengthOk: false, checksumOk, valid: false, error: `Length should be ${info.expectedLength} for ${cc}.` };
}
if (!checksumOk) {
return { ...info, lengthOk: true, checksumOk: false, valid: false, error: 'Checksum failed.' };
}
return { ...info, lengthOk: true, checksumOk: true, valid: true, error: null };
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →