JSFiddle - React, Tailwind, and code Playground

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>IBAN highlighter</title>
  </head>
  <body>
    Type your text with IBAN(s):
    <textarea id="source">Here is the IBAN: PT50 0002 0123 1234 5678 9015 4. And some text after.</textarea>
    <button type="button">Highlight!</button>
    <div id="target"></div>
  </body>
</html>

CSS

textarea {
  display: block;
  height: 50px;
  width: 500px;
  margin: 5px 0;
  resize: vertical;
  box-sizing: border-box;
}

#target {
  background: #eee;
  padding: 15px;
  margin-top: 20px;
  display: none;
  width: 500px;
  box-sizing: border-box;
}

.iban {
  background: yellow;
  padding: 2px;
}

JavaScript

const CODE_LENGTHS = {
    AD: 24, AE: 23, AT: 20, AZ: 28, BA: 20, BE: 16, BG: 22, BH: 22, BR: 29,
    CH: 21, CR: 21, CY: 28, CZ: 24, DE: 22, DK: 18, DO: 28, EE: 20, ES: 24,
    FI: 18, FO: 18, FR: 27, GB: 22, GI: 23, GL: 18, GR: 27, GT: 28, HR: 21,
    HU: 28, IE: 22, IL: 23, IS: 26, IT: 27, JO: 30, KW: 30, KZ: 20, LB: 28,
    LI: 21, LT: 20, LU: 20, LV: 21, MC: 27, MD: 24, ME: 22, MK: 19, MR: 27,
    MT: 31, MU: 30, NL: 18, NO: 15, PK: 24, PL: 28, PS: 29, PT: 25, QA: 29,
    RO: 24, RS: 22, SA: 24, SE: 24, SI: 19, SK: 24, SM: 27, TN: 24, TR: 26
};

// We use a regexp to find the suspects.
// IBAN starts from a 2-character country code followed by the checksum.
// So 4 symbols + the country specific number.
// The longest IBAN in this case is 31 symbols
// so the IBAN 'tail' is 31 - 4 = 26 characters. 
const MAX_LENGTH = Math.max(...Object.values(CODE_LENGTHS)) - 4;

// Let's say we want to highlight all the IBANs in the text.
const HighlightIBAN = text => text.replace(
    // Here is the magic. We search 2 letters followed by 2 digits
    // and followed by 26 meaningful characters.
    // The group (?:\W*[a-z0-9]) allows to ignore spaces, dots,
    // dashes or whatever a user may put as number group separators.
    new RegExp('[a-z]{2}[0-9]{2}(?:\\W*[a-z0-9]){0,' + MAX_LENGTH + '}', 'ig'),
    suspect => {
        // Here we have our suspect.
        // Just need to check if the IBAN is a IBAN indeed.
        const country = suspect.substr(0, 2).toUpperCase();
        const length = CODE_LENGTHS[country];

        // If the first 2 letters isn't a country code (not in the list)
        // we ignore the string found by the regex
        if(!length) return suspect;

        // Now we check if the finding contains the exact number
        // of meaningful symbols and extract the IBAN
        // using the same approach to ignore formatting
        const checkRegexp = new RegExp('^((?:\\W*[a-z0-9]){' + length + '})(.*)$', 'i');
        const parts =...