Skip to content

Regex Patterns Every Developer Should Know

A practical reference for the regular expression patterns you'll actually use in daily development, from validation to parsing to search-and-replace.

4 min read
Regular expression pattern with highlighted match groups on sample text

Regular expressions provoke a visceral reaction from developers. The syntax is cryptic, the behavior is surprising, and the memes about "now you have two problems" are well-earned. But regex is not going away. It appears in form validation, log parsing, search-and-replace, routing, linting rules, and dozens of other contexts.

The problem is not regex itself. It is that most developers learn regex through trial and error on StackOverflow instead of understanding the building blocks. Once you know the primitives, composing patterns becomes predictable.

The Building Blocks

Every regex pattern is a combination of six concepts: literals, character classes, quantifiers, anchors, groups, and alternation. That is the entire foundation.

jsjavascript
// Literals: match exact characters
/hello/          // matches "hello" in "say hello world"
 
// Character classes: match any character in a set
/[aeiou]/        // matches any vowel
/[^0-9]/         // matches any non-digit
/\d/             // shorthand for [0-9]
/\w/             // shorthand for [a-zA-Z0-9_]
/\s/             // shorthand for whitespace
 
// Quantifiers: how many times to match
/a*/             // 0 or more 'a'
/a+/             // 1 or more 'a'
/a?/             // 0 or 1 'a'
/a{3}/           // exactly 3 'a's
/a{2,5}/         // between 2 and 5 'a's
 
// Anchors: position in the string
/^start/         // starts with "start"
/end$/           // ends with "end"
/\bword\b/       // whole word "word"

That is it. Everything else is a composition of these six concepts. The cryptic-looking pattern ^\w+@[\w.-]+\.\w{2,}$ is just: start of string, one or more word characters, literal @, word characters/dots/hyphens, literal dot, 2+ word characters, end of string.

Validation Patterns

Form validation is the most common regex use case. These patterns handle the inputs you will see most frequently.

tstypescript
const patterns = {
  // Email — simplified but practical
  // (Full RFC 5322 compliance requires a parser, not regex)
  email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
 
  // URL — http/https with optional path
  url: /^https?:\/\/[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=]*$/,
 
  // Phone — flexible international format
  phone: /^\+?[\d\s\-().]{7,20}$/,
 
  // Slug — URL-safe identifier
  slug: /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
 
  // Hex color — 3 or 6 digit
  hexColor: /^#(?:[0-9a-fA-F]{3}){1,2}$/,
 
  // ISO date — YYYY-MM-DD
  isoDate: /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/,
};
 
function validate(value: string, pattern: RegExp): boolean {
  return pattern.test(value);
}
tstypescript
// ❌ Overly strict email regex — rejects valid addresses
const strictEmail = /^[a-z]+\.[a-z]+@[a-z]+\.com$/;
// Rejects: user+tag@domain.co.uk, UPPER@domain.com
 
// ✅ Practical email regex — catches obvious errors
const practicalEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// Validates structure without being overly restrictive

The email regex debate is eternal. The pragmatic answer: use a basic structural check with regex, then verify ownership by sending a confirmation email.

Capture Groups and Extraction

Groups () capture matched substrings for extraction. Named groups (?<name>) make the results self-documenting.

tstypescript
// Parse a log line with named groups
const logPattern = /^(?<timestamp>\d{4}-\d{2}-\d{2}T[\d:.]+Z)\s+(?<level>INFO|WARN|ERROR)\s+(?<message>.+)$/;
 
const line = '2020-12-02T14:30:00.000Z ERROR Connection timeout to database';
const match = line.match(logPattern);
 
if (match?.groups) {
  console.log(match.groups.timestamp); // 2020-12-02T14:30:00.000Z
  console.log(match.groups.level);     // ERROR
  console.log(match.groups.message);   // Connection timeout to database
}

Non-capturing groups (?:) group without capturing — useful when you need grouping for quantifiers but do not need the match:

tstypescript
// Match file extensions — capture the name, not the dot group
const filePattern = /^(?<name>[\w.-]+)\.(?<ext>tsx?|jsx?|css|html)$/;
 
const match = 'component.tsx'.match(filePattern);
// match.groups.name = "component"
// match.groups.ext = "tsx"

Search and Replace Patterns

Regex replacement is where the real productivity gains live. One regex replace can do the work of 50 manual edits.

tstypescript
// Convert camelCase to kebab-case
function toKebab(str: string): string {
  return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
// "backgroundColor" → "background-color"
 
// Strip HTML tags
function stripTags(html: string): string {
  return html.replace(/<[^>]*>/g, '');
}
 
// Mask sensitive data in logs
function maskEmail(text: string): string {
  return text.replace(
    /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
    '***@$2'
  );
}
// "Contact user@example.com" → "Contact ***@example.com"

The $1, $2 backreferences in replacement strings refer to captured groups. This makes structural transformations possible without parsing.

tstypescript
// Reformat dates from MM/DD/YYYY to YYYY-MM-DD
function reformatDate(text: string): string {
  return text.replace(
    /(\d{2})\/(\d{2})\/(\d{4})/g,
    '$3-$1-$2'
  );
}
// "12/02/2020" → "2020-12-02"

Lookahead and Lookbehind

Lookahead (?=) and lookbehind (?<=) match positions without consuming characters. They are assertions, not matches.

tstypescript
// Password validation — all conditions must be true
// At least 8 chars, one uppercase, one lowercase, one digit
const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
 
// Match numbers NOT followed by a unit (find raw numbers)
const rawNumbers = /\d+(?!\s*(?:px|em|rem|%))/g;
'16px 24 32rem 48'.match(rawNumbers); // ["24", "48"]
 
// Match currency amounts (number preceded by $)
const amounts = /(?<=\$)\d+(?:\.\d{2})?/g;
'Price: $19.99 and $5'.match(amounts); // ["19.99", "5"]

Lookaheads are supported everywhere. Lookbehinds require ES2018+ (Chrome 62+, Node 8.10+, Firefox 78+). For older environments, use capturing groups instead.

Performance and Safety

Regex can be dangerous. Catastrophic backtracking occurs when a pattern with nested quantifiers encounters input that forces exponential exploration.

tstypescript
// ❌ Catastrophic backtracking — O(2^n) on bad input
const bad = /^(a+)+$/;
bad.test('aaaaaaaaaaaaaaaaaaaaaaab'); // Hangs for seconds
 
// ❌ Another common disaster — nested repetition
const evil = /^(\w+\s?)+$/;
evil.test('a b c d e f g h i j k !'); // Exponential backtracking
 
// ✅ Avoid nested quantifiers on overlapping patterns
const safe = /^[a\s]+$/;
const alsoSafe = /^\w+(?:\s\w+)*$/;

Rules for safe regex:

  1. Never nest quantifiers on patterns that can match the same characters
  2. Use atomic groups or possessive quantifiers where available
  3. Set a timeout when running user-provided regex patterns
  4. Prefer non-greedy *? and +? when you need the shortest match
tstypescript
// When accepting regex from users, always set a timeout
function safeRegexTest(pattern: string, input: string, timeoutMs = 100): boolean {
  const start = performance.now();
  try {
    const regex = new RegExp(pattern);
    const result = regex.test(input);
    if (performance.now() - start > timeoutMs) {
      throw new Error('Regex execution exceeded timeout');
    }
    return result;
  } catch {
    return false;
  }
}

Key Takeaways

  1. Regex has six building blocks — literals, character classes, quantifiers, anchors, groups, and alternation cover everything
  2. Named groups make patterns readable — (?<name>...) is self-documenting, unlike $1 and $2
  3. Validation regex should be practical, not perfect — a basic email check plus a confirmation email beats an RFC-compliant regex
  4. Search-and-replace is the highest-leverage regex skill — one pattern can replace 50 manual edits
  5. Watch for catastrophic backtracking — nested quantifiers on overlapping patterns create exponential runtime
Wilfredo Rujel

Wilfredo Rujel

Full Stack Software Engineer

Share this postX