javascript
47 lines · 8 steps
How IBAN validation works in JavaScript
Validate an international bank account number by normalizing it, checking its shape and length, then running the ISO 7064 mod-97 checksum.
Explained by
highlit
1const IBAN_LENGTHS = {
2 DE: 22, FR: 27, GB: 22, ES: 24, IT: 27,
3 NL: 18, BE: 16, CH: 21, AT: 20, PT: 25,
4};
5
6function normalize(iban) {
7 return iban.replace(/\s+/g, '').toUpperCase();
8}
9
10function mod97(digits) {
11 let remainder = '';
12 for (const digit of digits) {
13 remainder += digit;
14 if (remainder.length >= 9) {
15 remainder = String(Number(remainder) % 97);
16 }
17 }
18 return Number(remainder) % 97;
19}
20
21function validateIban(input) {
22 const iban = normalize(input);
23
24 if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(iban)) {
25 return { valid: false, reason: 'malformed' };
26 }
27
28 const country = iban.slice(0, 2);
29 const expectedLength = IBAN_LENGTHS[country];
30 if (!expectedLength) {
31 return { valid: false, reason: 'unsupported_country' };
32 }
33 if (iban.length !== expectedLength) {
34 return { valid: false, reason: 'wrong_length' };
35 }
36
37 const rearranged = iban.slice(4) + iban.slice(0, 4);
38 const numeric = rearranged.replace(/[A-Z]/g, (c) => c.charCodeAt(0) - 55);
39
40 if (mod97(numeric) !== 1) {
41 return { valid: false, reason: 'checksum' };
42 }
43
44 return { valid: true, formatted: iban.match(/.{1,4}/g).join(' ') };
45}
46
47export { validateIban, normalize };
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1The mod-97 check catches transposed or mistyped digits that a simple length or format check would miss.
- 2Processing the number in chunks keeps the arithmetic within safe integer range instead of parsing one giant number.
- 3Layering cheap checks before expensive ones lets you fail fast and return a precise reason.
Related explainers
javascript
import { useDeferredValue, useMemo, useState } from "react"; function ProductSearch({ products }) { const [query, setQuery] = useState("");
Keeping search input snappy with useDeferredValue in React
concurrent-rendering
deferred-value
memoization
Intermediate
7 steps
typescript
import { InjectionToken, inject, Provider, isDevMode } from '@angular/core'; import { WINDOW } from './window.token'; export interface AnalyticsConfig {
Layered config with an Angular InjectionToken
dependency-injection
configuration
factory-provider
Intermediate
8 steps
python
import re from dataclasses import dataclass, field
Building a table of contents from Markdown
regular-expressions
parsing
slugification
Intermediate
9 steps
javascript
class ToastQueue { constructor(container, { duration = 4000, max = 3 } = {}) { this.container = container; this.duration = duration;
Building a rate-limited toast queue
queue
dom manipulation
throttling
Intermediate
8 steps
go
func UploadDocument(c *gin.Context) { fileHeader, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "file is required"})
Handling multipart uploads in Gin
multipart-upload
validation
error-handling
Intermediate
9 steps
javascript
import { useCallback, useState } from "react"; const MIN = 0; const MAX = 1000;
Building a dual-thumb price slider in React
controlled-components
state-clamping
usecallback
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-iban-validation-works-in-javascript-explained-javascript-6e3c/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.