How to Build a Regex Tester in Vanilla JavaScript

A step-by-step guide to building a real-time regular expression tester — no frameworks, no libraries, no build tools.

~7 min read • October 2026

javascript regex webdev tutorial

In This Tutorial

  1. Why Regex Matters
  2. Step 1: HTML Structure
  3. Step 2: The Regex Engine
  4. Step 3: Highlighting Matches
  5. Step 4: Capture Groups
  6. Step 5: Regex Flags
  7. The Complete Solution
  8. Try It Live

Why Regex Matters

Regular expressions are one of the most powerful yet misunderstood tools in a developer's arsenal. They're used for form validation, text parsing, data extraction, and pattern matching across virtually every programming language.

But regex is hard to get right. That's why building a regex tester — a tool that lets you type a pattern, test it against text, and see matches highlighted in real time — is both a practical exercise and a deep dive into how regular expressions actually work.

This tutorial assumes basic familiarity with regular expressions. If you know what \w+ means but aren't sure about lookahead assertions, you're ready.

Step 1: The HTML Structure

A regex tester needs three main components: a pattern input, a flags selector, and a text area to test against. Here's the skeleton:

HTML<div class="regex-tester">
  <div class="input-row">
    <input type="text" id="pattern" placeholder="Enter regex pattern..." />
    <select id="flags">
      <option value="g">g — Global</option>
      <option value="gi">gi — Global + Ignore Case</option>
      <option value="m">m — Multi-line</option>
      <option value="gs">gs — Global + Dotall</option>
    </select>
    <span id="status"></span>
  </div>

  <textarea id="testText" placeholder="Enter text to test against..."></textarea>

  <div id="results">
    <h3>Matches</h3>
    <div id="highlights"></div>
    <div id="captures"></div>
  </div>
</div>

Key design decisions:

Step 2: The Regex Engine

The core logic is deceptively simple: take the pattern, compile it into a RegExp object, and test it against the input text. The challenge is handling errors gracefully and updating the UI in real time.

JavaScriptconst patternInput = document.getElementById('pattern');
const flagsSelect = document.getElementById('flags');
const testTextInput = document.getElementById('testText');
const statusSpan = document.getElementById('status');
const highlightsDiv = document.getElementById('highlights');
const capturesDiv = document.getElementById('captures');

function testRegex() {
  const pattern = patternInput.value;
  const flags = flagsSelect.value;

  // Clear previous results
  statusSpan.textContent = '';
  statusSpan.style.color = '';
  highlightsDiv.innerHTML = '';
  capturesDiv.innerHTML = '';

  if (!pattern) {
    highlightsDiv.textContent = testTextInput.value;
    return;
  }

  let regex;
  try {
    regex = new RegExp(pattern, flags);
  } catch (e) {
    statusSpan.textContent = 'Invalid regex: ' + e.message;
    statusSpan.style.color = '#ef4444';
    return;
  }

  const text = testTextInput.value;
  const matches = [...text.matchAll(regex)];

  if (matches.length === 0) {
    highlightsDiv.textContent = text;
    statusSpan.textContent = 'No matches found';
    statusSpan.style.color = '#f59e0b';
    return;
  }

  // Display matches
  statusSpan.textContent = matches.length + ' match' + (matches.length !== 1 ? 'es' : '') + ' found';
  statusSpan.style.color = '#22c55e';
  renderHighlights(text, matches);
  renderCaptures(matches);
}

Key insight: String.prototype.matchAll() returns an iterator of all matches with capture groups. Spreading it into an array ([...text.matchAll(regex)]) gives us a concrete array we can iterate over. This is available in modern JavaScript (ES2020) and is far superior to the older RegExp.exec() loop.

Step 3: Highlighting Matches

Visual feedback is what makes a regex tester useful. We need to highlight matched portions of the text while escaping HTML entities to prevent injection.

JavaScriptfunction escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

function renderHighlights(text, matches) {
  let html = '';
  let lastIndex = 0;

  matches.forEach((match, index) => {
    // Text before this match
    html += escapeHtml(text.slice(lastIndex, match.index));
    // The matched text, highlighted
    html += '<span class="match">' + escapeHtml(match[0]) + '</span>';
    lastIndex = match.index + match[0].length;
  });

  // Remaining text after last match
  html += escapeHtml(text.slice(lastIndex));

  highlightsDiv.innerHTML = html;
}

This function walks through the text, inserting highlight spans at each match position. The result is a visual overlay that shows exactly where the regex matched — critical for debugging complex patterns.

The escapeHtml() function is essential. Without it, if the test text contains HTML characters like < or &, they would be interpreted as HTML tags, potentially breaking the layout or creating security issues.

Step 4: Capture Groups

The real power of regex comes from capture groups — numbered parenthetical expressions that extract specific parts of a match. A regex tester should display these clearly.

JavaScriptfunction renderCaptures(matches) {
  const hasGroups = matches.some(m => m.length > 1);
  if (!hasGroups) return;

  let html = '<h3>Capture Groups</h3><table><thead>';
  html += '<tr><th>#</th><th>Group</th><th>Value</th><th>Position</th></tr></thead><tbody>';

  matches.forEach((match, matchIdx) => {
    for (let i = 1; i < match.length; i++) {
      html += '<tr>';
      html += '<td>' + matchIdx + ':' + i + '</td>';
      html += '<td>Group ' + i + '</td>';
      html += '<td class="capture-group">' + escapeHtml(match[i] || '(empty)') + '</td>';
      html += '<td>' + (match.index + (i < match.length ? match[i].length : 0)) + '</td>';
      html += '</tr>';
    }
  });

  html += '</tbody></table>';
  capturesDiv.innerHTML = html;
}

Each match array has:

For example, the pattern (\d{4})-(\d{2})-(\d{2}) against 2026-10-02 produces:

Match[
  "2026-10-02",    // full match
  "2026",          // group 1: year
  "10",            // group 2: month
  "02",            // group 3: day
  index: 0,
  input: "2026-10-02",
  groups: undefined
]

Step 5: Regex Flags and Edge Cases

JavaScript regex supports several flags that dramatically change matching behavior:

JavaScript// 'g' — Global: find all matches, not just the first
'hello world hello'.match(/hello/g)
// → ["hello", "hello"]

// 'i' — Ignore case: case-insensitive matching
'Hello'.match(/hello/i)
// → ["Hello"]

// 'm' — Multi-line: ^ and $ match line boundaries
'line1\nline2'.match(/^line/gm)
// → ["line", "line"]

// 's' — Dotall: . matches newlines too (ES2018+)
'a.b'.match(/a.b/s)
// → ["a.b"]

// 'u' — Unicode: proper handling of Unicode code points
'\uD83D\uDE00'.match(/\p{Emoji}/gu)
// → ["😀"]

The g flag changes how RegExp.exec() and matchAll() behave — without it, they only return the first match. With g, they return all matches. This is the most commonly used flag in regex testers.

Edge cases to handle:

The Complete Solution

Here's everything together — copy this into an HTML file and open it in your browser:

index.html<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Regex Tester</title>
  <style>
    /* Paste the CSS from the header section */
  </style>
</head>
<body>
  <div class="regex-tester">
    <!-- Paste the HTML from Step 1 -->
  </div>
  <script>
    // Paste the JavaScript from Steps 2-5
    patternInput.addEventListener('input', testRegex);
    flagsSelect.addEventListener('change', testRegex);
    testTextInput.addEventListener('input', testRegex);
  </script>
</body>
</html>

The full, working implementation is available as a single HTML file — no build step required. It includes:

Try It Live

The fully working Regex Tester is live and free to use:

Open Regex Tester → View This Tutorial

All tools on k1r4.space are free, open-source, and require no signup, tracking, or ads. They're built with vanilla HTML, CSS, and JavaScript — no frameworks, no build tools.

What We Learned

Building a regex tester taught us several important concepts: