React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

typescriptregexregular-expressionsvalidationstring-matchingpatterns

Regular Expressions Reference

A comprehensive reference for JavaScript/TypeScript regular expressions — syntax, flags, methods, and real-world patterns commonly used in React applications.

Event Reference

MethodReturnsDescription
regex.test(str)booleanReturns true if the pattern matches anywhere in the string
regex.exec(str)RegExpExecArray | nullReturns match details (groups, index) or null
str.match(regex)RegExpMatchArray | nullReturns matches — behavior changes with g flag
str.matchAll(regex)IterableIterator<RegExpMatchArray>Returns iterator of all matches (requires g flag)
str.search(regex)numberReturns index of first match, or -1
str.replace(regex, replacement)stringReplaces first match (or all with g flag)
str.replaceAll(regex, replacement)stringReplaces all matches (requires g flag)
str.split(regex)string[]Splits string by regex matches

Recipe

Quick-reference — most common regex operations in TypeScript.

// Test if a string matches
const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test("user@example.com"); // true
 
// Extract matches
const matches = "2024-01-15".match(/(\d{4})-(\d{2})-(\d{2})/);
// matches[1] = "2024", matches[2] = "01", matches[3] = "15"
 
// Named capture groups
const result = "2024-01-15".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
// result?.groups?.year = "2024"
 
// Replace with pattern
const slug = "Hello World! Foo".replace(/[^a-zA-Z0-9]+/g, "-").toLowerCase();
// "hello-world-foo"
 
// Split by pattern
const words = "one,  two;  three".split(/[,;]\s*/);
// ["one", "two", "three"]

When to reach for this: Input validation, text parsing, URL routing, search/filter logic, and data transformation in forms and utilities.

Working Example

"use client";
 
import { useState } from "react";
 
interface ValidationResult {
  field: string;
  value: string;
  isValid: boolean;
  message: string;
}
 
const VALIDATORS: Record<string, { pattern: RegExp; message: string }> = {
  email: {
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    message: "Must be a valid email address",
  },
  phone: {
    pattern: /^\+?[\d\s\-()]{7,15}$/,
    message: "Must be a valid phone number (7-15 digits)",
  },
  url: {
    pattern: /^https?:\/\/[^\s/$.?#].[^\s]*$/i,
    message: "Must be a valid URL starting with http(s)://",
  },
  zipCode: {
    pattern: /^\d{5}(-\d{4})?$/,
    message: "Must be a valid US zip code (12345 or 12345-6789)",
  },
  password: {
    pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/,
    message: "Min 8 chars, 1 uppercase, 1 lowercase, 1 digit, 1 special char",
  },
};
 
export default function RegexValidationDemo() {
  const [results, setResults] = useState<ValidationResult[]>([]);
  const [input, setInput] = useState("");
  const [selected, setSelected] = useState("email");
 
  function validate() {
    const { pattern, message } = VALIDATORS[selected];
    const isValid = pattern.test(input);
    setResults((prev) => [
      { field: selected, value: input, isValid, message: isValid ? "Valid" : message },
      ...prev,
    ]);
  }
 
  return (
    <div className="space-y-4 p-4 max-w-md">
      <select
        value={selected}
        onChange={(e) => setSelected(e.target.value)}
        className="border rounded px-2 py-1 w-full"
      >
        {Object.keys(VALIDATORS).map((key) => (
          <option key={key} value={key}>{key}</option>
        ))}
      </select>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder={`Enter ${selected}...`}
        className="border rounded px-2 py-1 w-full"
      />
      <button onClick={validate} className="bg-blue-600 text-white px-4 py-1 rounded">
        Validate
      </button>
      <ul className="space-y-1 text-sm">
        {results.map((r, i) => (
          <li key={i} className={r.isValid ? "text-green-600" : "text-red-600"}>
            <strong>{r.field}:</strong> &quot;{r.value}&quot; — {r.message}
          </li>
        ))}
      </ul>
    </div>
  );
}

What this demonstrates:

  • Using RegExp.test() for boolean validation
  • Storing regex patterns in a typed lookup object
  • Common real-world validation patterns (email, phone, URL, zip, password)

Deep Dive

Regex Syntax Reference

Character Classes

// Predefined
/\d/     // Digit [0-9]
/\D/     // Non-digit [^0-9]
/\w/     // Word char [a-zA-Z0-9_]
/\W/     // Non-word char
/\s/     // Whitespace (space, tab, newline)
/\S/     // Non-whitespace
/./      // Any char except newline
 
// Custom
/[abc]/    // Any of a, b, or c
/[a-z]/    // Range: a through z
/[^abc]/   // NOT a, b, or c (negation)
/[a-zA-Z]/ // Any letter
/[0-9]/    // Same as \d

Quantifiers

/a*/     // 0 or more
/a+/     // 1 or more
/a?/     // 0 or 1
/a{3}/   // Exactly 3
/a{2,5}/ // 2 to 5
/a{2,}/  // 2 or more
 
// Greedy vs Lazy
/".+"/   // Greedy: matches "foo" and "bar" as one match in: "foo" and "bar"
/".+?"/  // Lazy: matches "foo" then "bar" separately

Anchors and Boundaries

/^hello/    // Starts with "hello"
/world$/    // Ends with "world"
/\bhello\b/ // Whole word "hello" (word boundary)
/\Bhello/   // NOT at a word boundary

Groups and Alternation

// Capture group
/(foo|bar)/           // Matches "foo" or "bar", captures it
/(\d{3})-(\d{4})/    // Capture area code and number separately
 
// Non-capturing group
/(?:foo|bar)/         // Groups without capturing
 
// Named capture group
/(?<area>\d{3})-(?<number>\d{4})/
 
// Backreference
/(\w+)\s+\1/         // Matches repeated words: "the the"

Lookahead and Lookbehind

// Positive lookahead: match "foo" followed by "bar"
/foo(?=bar)/          // "foobar" ✓  "foobaz" ✗
 
// Negative lookahead: match "foo" NOT followed by "bar"
/foo(?!bar)/          // "foobaz" ✓  "foobar" ✗
 
// Positive lookbehind: match "bar" preceded by "foo"
/(?<=foo)bar/         // "foobar" ✓  "bazbar" ✗
 
// Negative lookbehind: match "bar" NOT preceded by "foo"
/(?<!foo)bar/         // "bazbar" ✓  "foobar" ✗

Flags

/pattern/g   // Global — find all matches, not just the first
/pattern/i   // Case-insensitive
/pattern/m   // Multiline — ^ and $ match line starts/ends
/pattern/s   // Dotall — . matches newlines too
/pattern/u   // Unicode — proper Unicode support
/pattern/d   // Indices — include start/end indices in results
/pattern/v   // Unicode sets (ES2024) — set operations in character classes
 
// Combine flags
/pattern/gi  // Global + case-insensitive

JavaScript Methods In Detail

test() — Boolean Check

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 
emailRegex.test("user@site.com");  // true
emailRegex.test("not-an-email");   // false
 
// WARNING: test() with /g flag is stateful
const re = /a/g;
re.test("abc"); // true  (lastIndex = 1)
re.test("abc"); // false (lastIndex = 0, reset)
re.test("abc"); // true  (cycle repeats)

exec() — Detailed Match Info

const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const result = dateRegex.exec("Date: 2024-03-15");
 
if (result) {
  result[0];             // "2024-03-15" (full match)
  result[1];             // "2024" (group 1)
  result.index;          // 6 (position in string)
  result.groups?.year;   // "2024"
  result.groups?.month;  // "03"
  result.groups?.day;    // "15"
}

match() — String Method

// Without /g: same as exec()
"Price: $42.50".match(/\$(\d+\.\d{2})/);
// ["$42.50", "42.50", index: 7, groups: undefined]
 
// With /g: returns array of all matches (no groups)
"cat bat hat".match(/[a-z]at/g);
// ["cat", "bat", "hat"]

matchAll() — Iterate All Matches with Groups

const text = "Jan 15, Feb 20, Mar 5";
const regex = /(?<month>[A-Z][a-z]+)\s+(?<day>\d+)/g;
 
for (const match of text.matchAll(regex)) {
  console.log(`${match.groups?.month} ${match.groups?.day}`);
}
// "Jan 15"
// "Feb 20"
// "Mar 5"
 
// Or convert to array
const allMatches = [...text.matchAll(regex)];

replace() and replaceAll()

// Simple replacement
"hello world".replace(/world/, "TypeScript"); // "hello TypeScript"
 
// Global replacement
"aabbcc".replace(/b/g, "X"); // "aaXXcc"
 
// With capture groups ($1, $2, etc.)
"John Smith".replace(/(\w+)\s(\w+)/, "$2, $1"); // "Smith, John"
 
// With named groups
"2024-01-15".replace(
  /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
  "$<m>/$<d>/$<y>"
); // "01/15/2024"
 
// With a function
"hello world".replace(/\b\w/g, (char) => char.toUpperCase());
// "Hello World"
 
// Complex function replacement
"img_001.png, img_002.jpg".replace(
  /img_(\d+)\.(png|jpg)/g,
  (_match, num, ext) => `photo-${parseInt(num)}.${ext}`
);
// "photo-1.png, photo-2.jpg"

split() — Split by Pattern

// Split by multiple delimiters
"one, two; three  four".split(/[,;\s]+/);
// ["one", "two", "three", "four"]
 
// Split keeping the delimiter (capture group)
"one+two-three".split(/([+-])/);
// ["one", "+", "two", "-", "three"]
 
// Limit results
"a-b-c-d-e".split(/-/, 3);
// ["a", "b", "c"]

Variations

Variation 1: Email Validation

// Basic email validation
const basicEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 
// Stricter email (common real-world pattern)
const stricterEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
 
basicEmail.test("user@example.com");      // true
basicEmail.test("user@sub.domain.co.uk"); // true
basicEmail.test("user@.com");             // false

Variation 2: URL Parsing

const urlRegex = /^(?<protocol>https?):\/\/(?<host>[^/:]+)(?::(?<port>\d+))?(?<path>\/[^?#]*)?(?:\?(?<query>[^#]*))?(?:#(?<fragment>.*))?$/;
 
const parsed = urlRegex.exec("https://example.com:8080/api/users?page=2#top");
if (parsed?.groups) {
  parsed.groups.protocol; // "https"
  parsed.groups.host;     // "example.com"
  parsed.groups.port;     // "8080"
  parsed.groups.path;     // "/api/users"
  parsed.groups.query;    // "page=2"
  parsed.groups.fragment; // "top"
}

Variation 3: Password Strength Checker

function checkPasswordStrength(password: string): string[] {
  const checks = [
    { regex: /.{8,}/, label: "At least 8 characters" },
    { regex: /[a-z]/, label: "Lowercase letter" },
    { regex: /[A-Z]/, label: "Uppercase letter" },
    { regex: /\d/, label: "A digit" },
    { regex: /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/, label: "Special character" },
    { regex: /^(?!.*(.)\1{2})/, label: "No 3+ repeated characters" },
  ];
  return checks.filter((c) => !c.regex.test(password)).map((c) => c.label);
}
 
checkPasswordStrength("weak");     // ["At least 8 characters", "Uppercase letter", "A digit", "Special character"]
checkPasswordStrength("Str0ng!Pass"); // []

Variation 4: Slug Generator

function toSlug(text: string): string {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, "")   // Remove non-word chars (except spaces and hyphens)
    .replace(/[\s_]+/g, "-")    // Replace spaces/underscores with hyphens
    .replace(/-+/g, "-")        // Collapse multiple hyphens
    .replace(/^-|-$/g, "");     // Trim leading/trailing hyphens
}
 
toSlug("Hello, World! This is a Test");  // "hello-world-this-is-a-test"
toSlug("  --Foo  &  Bar-- ");            // "foo--bar"

Variation 5: Extract Numbers from Text

function extractNumbers(text: string): number[] {
  return [...text.matchAll(/-?\d+\.?\d*/g)].map((m) => parseFloat(m[0]));
}
 
extractNumbers("Price: $42.50, Qty: 3, Discount: -5.25");
// [42.5, 3, -5.25]

Variation 6: Template String Interpolation

function interpolate(template: string, vars: Record<string, string>): string {
  return template.replace(/\{\{(\w+)\}\}/g, (_match, key: string) => vars[key] ?? `{{${key}}}`);
}
 
interpolate("Hello {{name}}, welcome to {{place}}!", {
  name: "Alice",
  place: "Wonderland",
});
// "Hello Alice, welcome to Wonderland!"

Variation 7: Highlight Search Matches (React)

function highlightMatches(text: string, query: string): (string | JSX.Element)[] {
  if (!query.trim()) return [text];
  const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const regex = new RegExp(`(${escaped})`, "gi");
  const parts = text.split(regex);
  return parts.map((part, i) =>
    regex.test(part) ? <mark key={i} className="bg-yellow-200">{part}</mark> : part
  );
}
 
// Usage: <p>{highlightMatches("Hello World", "world")}</p>
// Renders: Hello <mark>World</mark>

Variation 8: Sanitize HTML Entities

function stripHtmlTags(html: string): string {
  return html.replace(/<[^>]*>/g, "");
}
 
stripHtmlTags("<p>Hello <strong>world</strong></p>"); // "Hello world"
 
function escapeRegex(str: string): string {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
 
escapeRegex("Price: $10.00 (USD)"); // "Price: \\$10\\.00 \\(USD\\)"

Variation 9: Parse CSV Line

function parseCSVLine(line: string): string[] {
  const result: string[] = [];
  const regex = /(?:^|,)("(?:[^"]|"")*"|[^,]*)/g;
  let match: RegExpExecArray | null;
  while ((match = regex.exec(line)) !== null) {
    let value = match[1];
    if (value.startsWith('"') && value.endsWith('"')) {
      value = value.slice(1, -1).replace(/""/g, '"');
    }
    result.push(value);
  }
  return result;
}
 
parseCSVLine('Alice,30,"New York, NY","She said ""hi"""');
// ["Alice", "30", "New York, NY", 'She said "hi"']

Variation 10: Phone Number Formatter

function formatPhone(raw: string): string {
  const digits = raw.replace(/\D/g, "");
  const match = digits.match(/^(\d{1,3})?(\d{3})(\d{3})(\d{4})$/);
  if (!match) return raw;
  const [, country, area, prefix, line] = match;
  return country ? `+${country} (${area}) ${prefix}-${line}` : `(${area}) ${prefix}-${line}`;
}
 
formatPhone("12125551234");  // "+1 (212) 555-1234"
formatPhone("2125551234");   // "(212) 555-1234"

Variation 11: Credit Card Detection

const CARD_PATTERNS: Record<string, RegExp> = {
  visa: /^4\d{12}(?:\d{3})?$/,
  mastercard: /^5[1-5]\d{14}$/,
  amex: /^3[47]\d{13}$/,
  discover: /^6(?:011|5\d{2})\d{12}$/,
};
 
function detectCardType(number: string): string | null {
  const cleaned = number.replace(/\D/g, "");
  for (const [type, pattern] of Object.entries(CARD_PATTERNS)) {
    if (pattern.test(cleaned)) return type;
  }
  return null;
}
 
detectCardType("4111 1111 1111 1111"); // "visa"
detectCardType("5500 0000 0000 0004"); // "mastercard"

Variation 12: Extract Hashtags and Mentions

function extractHashtags(text: string): string[] {
  return [...text.matchAll(/#(\w+)/g)].map((m) => m[1]);
}
 
function extractMentions(text: string): string[] {
  return [...text.matchAll(/@(\w+)/g)].map((m) => m[1]);
}
 
const tweet = "Hey @alice and @bob, check out #TypeScript #React!";
extractHashtags(tweet);  // ["TypeScript", "React"]
extractMentions(tweet);  // ["alice", "bob"]

Variation 13: IP Address Validation

const ipv4Regex = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
 
ipv4Regex.test("192.168.1.1");   // true
ipv4Regex.test("256.1.1.1");     // false
ipv4Regex.test("10.0.0.255");    // true

Variation 14: Markdown Link Extractor

function extractMarkdownLinks(md: string): Array<{ text: string; url: string }> {
  return [...md.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)].map((m) => ({
    text: m[1],
    url: m[2],
  }));
}
 
extractMarkdownLinks("Visit [Google](https://google.com) or [GitHub](https://github.com)");
// [{ text: "Google", url: "https://google.com" }, { text: "GitHub", url: "https://github.com" }]

Variation 15: Camel Case / Snake Case Conversion

function camelToSnake(str: string): string {
  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
}
 
function snakeToCamel(str: string): string {
  return str.replace(/_([a-z])/g, (_match, letter: string) => letter.toUpperCase());
}
 
camelToSnake("backgroundColor");  // "background_color"
snakeToCamel("background_color"); // "backgroundColor"
 
function toKebabCase(str: string): string {
  return str
    .replace(/([a-z])([A-Z])/g, "$1-$2")
    .replace(/[\s_]+/g, "-")
    .toLowerCase();
}
 
toKebabCase("backgroundColor"); // "background-color"
toKebabCase("Hello World");     // "hello-world"

TypeScript Notes

// RegExp type
const pattern: RegExp = /\d+/g;
 
// RegExpMatchArray — returned by String.match() / RegExp.exec()
const result: RegExpMatchArray | null = "abc123".match(/(\d+)/);
if (result) {
  const full: string = result[0];
  const group: string = result[1];
  const idx: number = result.index ?? 0;
}
 
// RegExpExecArray with named groups
const dateMatch = /(?<year>\d{4})-(?<month>\d{2})/.exec("2024-03");
if (dateMatch?.groups) {
  // groups is Record<string, string> — requires optional chaining
  const year: string | undefined = dateMatch.groups.year;
}
 
// Type-safe validator map
interface Validator {
  pattern: RegExp;
  message: string;
}
 
const validators: Record<string, Validator> = {
  email: { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: "Invalid email" },
};
 
// Template literal type with regex validation (compile-time only)
type HexColor = `#${string}`;
function setColor(color: HexColor) { /* ... */ }
setColor("#ff0000"); // OK
// setColor("red");  // Type error

Gotchas

  • test() with /g flag is stateful — The regex object tracks lastIndex. Calling test() repeatedly on different strings gives unexpected results. Fix: Omit the g flag when using test(), or create a new regex each time.

  • Forgetting to escape special characters — Characters like ., *, +, ?, (, ), [, {, $, ^, |, \ have special meaning. Fix: Use a helper: str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") to escape user input before building a regex.

  • Greedy matching captures too much/".+"/.exec('"foo" and "bar"') matches "foo" and "bar" as one match. Fix: Use lazy quantifier ".+?" to match the shortest possible string.

  • match() with /g drops capture groups — With the global flag, match() returns only full matches, not groups. Fix: Use matchAll() instead when you need group information with global matching.

  • Catastrophic backtracking — Patterns like /(a+)+$/ on long non-matching strings can freeze the browser. Fix: Avoid nested quantifiers on the same characters. Use atomic patterns or test with ReDoS checkers.

  • \b doesn't work with Unicode — Word boundaries use ASCII definition of "word character" ([a-zA-Z0-9_]). Accented characters like é are treated as non-word. Fix: Use the /u flag and explicit boundaries when working with international text.

  • Newlines don't match . — By default, . matches any character except \n. Fix: Use the /s (dotall) flag to make . match newlines, or use [\s\S] as a universal alternative.

  • Forgetting ^ and $ anchors in validation/\d+/.test("abc123xyz") returns true because it finds digits anywhere. Fix: Use ^ and $ to enforce full-string matching: /^\d+$/.

  • replace() only replaces the first match — Without the g flag, only the first occurrence is replaced. Fix: Always use /g flag or replaceAll() when you want to replace all occurrences.

  • Building regex from user input without escaping — If a user types (hello) into a search box and you use new RegExp(input), it creates a capture group instead of matching literal parentheses. Fix: Always escape user input before passing to new RegExp().

Alternatives

AlternativeUse WhenDon't Use When
String.includes()Simple substring check without pattern matchingYou need wildcards or pattern logic
String.startsWith() / endsWith()Checking prefix or suffixThe prefix/suffix varies by pattern
Zod .regex()Schema validation in forms with zodYou need to extract or transform matched content
URL / URLSearchParams APIParsing URLsMatching arbitrary URL-like patterns in text
Intl.NumberFormatFormatting numbers for displayExtracting numbers from mixed text
Parser libraries (e.g., date-fns)Complex date/time parsingSimple date format matching

FAQs

What is the difference between test() and match()?
  • test() is a RegExp method that returns a boolean — use it when you only need to know if a pattern matches
  • match() is a String method that returns the match details (full match, groups, index) or null
  • test() is faster when you don't need match details
  • Both are stateful with the /g flag — test() updates lastIndex on the regex object
When should I use exec() vs matchAll()?
  • exec() returns one match at a time — call it in a loop with a /g regex to iterate
  • matchAll() returns an iterator of all matches at once — cleaner and less error-prone
  • matchAll() requires the /g flag; exec() works with or without it
  • Prefer matchAll() in modern code — it avoids the stateful lastIndex pitfall of exec() loops
How do I safely use user input in a regex?
  • Always escape special regex characters before using new RegExp(userInput)
  • Use this escape function: input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
  • Never pass raw user input to new RegExp() — it can cause syntax errors or ReDoS attacks
  • Consider using String.includes() for simple substring matching instead
What are named capture groups and why use them?
  • Syntax: (?<name>pattern) — assigns a name to a capture group
  • Access via match.groups.name instead of positional match[1]
  • Makes code more readable and less brittle when the regex changes
  • Available in ES2018+ — supported in all modern browsers and Node.js 10+
What is catastrophic backtracking and how do I avoid it?
  • Occurs when a regex has nested quantifiers like (a+)+ that create exponential matching paths
  • On non-matching input, the engine tries every combination before failing — can freeze the browser
  • Avoid patterns like (x+)+, (x+)*, (x*)* on the same character set
  • Test patterns with a ReDoS checker tool before using in production
  • Use atomic groups or possessive quantifiers where available
How do lookaheads and lookbehinds differ from regular groups?
  • Lookaheads (?=...) and lookbehinds (?<=...) are zero-width — they assert a condition without consuming characters
  • Regular groups (...) consume the matched text and include it in the result
  • Use positive lookahead (?=...) to match "followed by" without including the following text
  • Use negative lookahead (?!...) to match "NOT followed by"
  • Lookbehinds have the same logic but check what comes before the current position
Why does my regex work in a tester but not in TypeScript?
  • Online testers may use different regex flavors (PCRE, Python, etc.) with features JavaScript lacks
  • JavaScript doesn't support possessive quantifiers (a++), atomic groups ((?>...)), or some PCRE syntax
  • The /s (dotall) and /d (indices) flags were added in ES2018/ES2022 — older environments may not support them
  • Check that you're using the correct escape sequences — in new RegExp("\\d") you need double backslashes
How should I type regex results in TypeScript?
  • RegExp.exec() and String.match() return RegExpMatchArray | null — always null-check
  • Named groups are typed as Record<string, string> on the groups property — use optional chaining
  • There's no built-in way to type specific group names — use a wrapper or assertion if needed
  • For matchAll(), the return type is IterableIterator<RegExpMatchArray>
What is the /u flag and when do I need it?
  • The /u flag enables full Unicode matching — without it, regex treats surrogate pairs as two characters
  • Needed when matching emoji, CJK characters, or any text outside the Basic Multilingual Plane
  • Enables \p{...} Unicode property escapes like \p{Letter} or \p{Emoji}
  • Always use /u when working with international text to avoid subtle character-matching bugs
Should I use regex for email validation?
  • A basic regex like /^[^\s@]+@[^\s@]+\.[^\s@]+$/ catches most typos and is fine for UX
  • No regex can fully validate an email per RFC 5322 — the spec is extremely complex
  • For production, combine a simple regex check with a confirmation email or server-side validation
  • Libraries like Zod provide .email() validators that handle common edge cases
How do I match across multiple lines?
  • By default, ^ and $ match start/end of the entire string
  • Use the /m (multiline) flag to make ^ and $ match start/end of each line
  • Use the /s (dotall) flag to make . match newline characters
  • Alternatively, use [\s\S] instead of . to match any character including newlines without the /s flag
What is the difference between replace() and replaceAll()?
  • replace() without /g replaces only the first match
  • replace() with /g replaces all matches
  • replaceAll() always replaces all matches — requires /g flag if passed a regex
  • replaceAll() with a string argument replaces all occurrences without needing regex
  • replaceAll() was added in ES2021 — use replace(/pattern/g, ...) for older environments