Developer

REGEX TESTER

Test regular expressions with live match highlighting, capture groups, and quick presets.

Quick Presets
/
/
Flags:
Matches Highlighted
0
matches found
0
capture groups
Match Details
Developer Reference

Core Algorithm & Standalone Script

Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.

const PRESETS = [
    { name: 'Email', pattern: '[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}', test: 'Contact us at hello@example.com or support@toolpad.cc' },
    { name: 'URL', pattern: 'https?:\\/\\/[^\\s/$.?#].[^\\s]*', test: 'Visit https://toolpad.cc or http://example.com/path?q=1' },
    { name: 'Phone (US)', pattern: '\\(?\\d{3}\\)?[\\s.\\-]?\\d{3}[\\s.\\-]?\\d{4}', test: 'Call (800) 555-1234 or 800.555.5678' },
    { name: 'Date (YYYY-MM-DD)', pattern: '\\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01])', test: 'Meeting on 2024-03-15 and deadline 2024-12-31' },
    { name: 'IPv4', pattern: '(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)', test: 'Server at 192.168.1.100 and gateway 10.0.0.1' },
    { name: 'Credit Card', pattern: '\\d{4}[\\s\\-]?\\d{4}[\\s\\-]?\\d{4}[\\s\\-]?\\d{4}', test: 'Card: 4111 1111 1111 1111 or 5500-0000-0000-0004' },
    { name: 'Hex Color', pattern: '#(?:[0-9a-fA-F]{3}){1,2}\\b', test: 'Colors: #ff2200 #fff #00c896 #4169e1' },
    { name: 'Postal Code (US)', pattern: '\\b\\d{5}(?:-\\d{4})?\\b', test: 'Zip codes: 10001 and 90210-4321' },
  ];

  const presetGrid = document.getElementById('presetGrid');
  PRESETS.forEach(p => {
    const btn = document.createElement('button');
    btn.className = 'preset-btn';
    btn.textContent = p.name;
    btn.onclick = () => {
      document.getElementById('patternInput').value = p.pattern;
      if (p.test && !document.getElementById('testInput').value) {
        document.getElementById('testInput').value = p.test;
      }
      runRegex();
    };
    presetGrid.appendChild(btn);
  });

  function getFlags() {
    return Array.from(document.querySelectorAll('.flag-btn.active')).map(b => b.dataset.flag).join('');
  }

  function toggleFlag(btn) {
    btn.classList.toggle('active');
    runRegex();
  }

  function clearAll() {
    document.getElementById('patternInput').value = '';
    document.getElementById('testInput').value = '';
    runRegex();
  }

  function escapeHtml(str) {
    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  }

  function runRegex() {
    const pattern = document.getElementById('patternInput').value;
    const test = document.getElementById('testInput').value;
    const errEl = document.getElementById('regexError');
    const highlightEl = document.getElementById('highlightOutput');
    const matchListEl = document.getElementById('matchList');

    errEl.textContent = '';
    document.getElementById('patternInput').classList.remove('invalid');

    if (!pattern) {
      highlightEl.textContent = test;
      document.getElementById('matchCount').textContent = '0';
      document.getElementById('groupCount').textContent = '0';
      matchListEl.innerHTML = '';
      return;
    }

    let flags = getFlags();
    // Ensure g flag for all-match behavior
    let re;
    try {
      re = new RegExp(pattern, flags.includes('g') ? flags : flags + 'g');
    } catch(e) {
      errEl.textContent = e.message;
      document.getElementById('patternInput').classList.add('invalid');
      highlightEl.textContent = test;
      document.getElementById('matchCount').textContent = '0';
      document.getElementById('groupCount').textContent = '0';
      matchListEl.innerHTML = '';
      return;
    }

    // Collect matches
    const matches = [];
    let m;
    re.lastIndex = 0;
    while ((m = re.exec(test)) !== null) {
      matches.push({ value: m[0], index: m.index, end: m.index + m[0].length, groups: Array.from(m).slice(1) });
      if (!flags.includes('g')) break;
      if (m[0].length === 0) re.lastIndex++;
    }

    document.getElementById('matchCount').textContent = matches.length;
    const maxGroups = matches.reduce((a, m) => Math.max(a, m.groups.length), 0);
    document.getElementById('groupCount').textContent = maxGroups;

    // Highlight
    if (matches.length === 0) {
      highlightEl.textContent = test;
    } else {
      let html = '';
      let lastIdx = 0;
      matches.forEach(m => {
        html += escapeHtml(test.slice(lastIdx, m.index));
        html += '<mark>' + escapeHtml(m.value) + '</mark>';
        lastIdx = m.end;
      });
      html += escapeHtml(test.slice(lastIdx));
      highlightEl.innerHTML = html;
    }

    // Match list
    if (matches.length === 0) {
      matchListEl.innerHTML = '<div class="no-matches">No matches found.</div>';
    } else {
      matchListEl.innerHTML = '';
      matches.forEach((m, i) => {
        const item = document.createElement('div');
        item.className = 'match-item';
        let groupsHtml = '';
        if (m.groups.length > 0 && m.groups.some(g => g !== undefined)) {
          groupsHtml = '<div class="match-groups">' + m.groups.map((g, gi) =>
            `<div class="match-group">Group ${gi + 1}: <span>${g !== undefined ? escapeHtml(String(g)) : '<em>undefined</em>'}</span></div>`
          ).join('') + '</div>';
        }
        item.innerHTML = `
          <div class="match-item-header">
            <span class="match-idx">Match ${i + 1}</span>
            <span class="match-pos">pos ${m.index}–${m.end}</span>
          </div>
          <div class="match-val">${escapeHtml(m.value)}</div>
          ${groupsHtml}
        `;
        matchListEl.appendChild(item);
      });
    }
  }

  // Init
  document.getElementById('testInput').value = 'The quick brown fox jumps over the lazy dog.\nEmail: test@example.com\nDate: 2024-01-15';
  runRegex();