Developer Reference / ECMAScript Regular Expressions

JavaScript Regular Expressions: A Practical Guide

A comprehensive engineering reference for JavaScript RegExp syntax, anchors, quantifiers, character classes, named capturing groups, lookaround assertions, runtime methods, and catastrophic backtracking (ReDoS) prevention.

Specification
ECMAScript 2026 (ES15+)
Interactive Companion
Live JS Regex Tester
Archive Heritage
REJAX / Aphex Creations
Maintainer
Web Dev Awards Editorial
Interactive Companion Utility

TEST JAVASCRIPT REGULAR EXPRESSIONS LIVE IN YOUR BROWSER

Want to experiment with the patterns in this guide right now? Launch our interactive JavaScript Regex Tester with live match highlighting, capture group inspection, character positions, and String.replace() previews.

Open Regex Tester →
01 / Core Fundamentals

Syntax Reference: Literals vs RegExp Constructor

In JavaScript, regular expressions are first-class RegExp objects that can be created using either a compile-time regular expression literal or the dynamic new RegExp() constructor.

Literal Notation vs Dynamic Constructor (javascript)
// 1. Regular expression literal (compiled once when script is parsed)
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/i;

// 2. Dynamic constructor (use when building patterns from runtime variables)
// Note: Backslashes inside string literals must be double-escaped ("\\d")
const allowedExt = ["svg", "png", "webp"].join("|");
const fileRegex = new RegExp(`^([\\w-]+)\\.(${allowedExt})$`, "i");

When interpolating untrusted user input into a dynamic new RegExp() call, always escape the 14 ECMAScript metacharacters (. * + ? ^ $ { } ( ) | [ ] \) using RegExp.escape(input) (or a replacement helper input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).

02 / Position Matching

Anchors & Word Boundaries

Anchors do not match characters; instead, they assert a specific cursor boundary before, after, or between characters:

AnchorMeaningExample
^Start of string (or start of line when m flag is active)/^https:\/\//
$End of string (or end of line when m flag is active)/\.json$/
\bWord boundary (transition between \w and \W or start/end)/\bcat\b/ (matches "cat", not "category")
\BNon-word boundary (cursor is between two word or two non-word chars)/\Bscript\b/ (matches "JavaScript")
03 / Repetition Control

Quantifiers: Greedy vs Lazy Matching

Quantifiers control how many times the preceding atom or group may repeat. By default, all JavaScript quantifiers are greedy—they consume as many characters as possible while still allowing the overall pattern to succeed. Appending a question mark (?) after any quantifier makes it lazy (non-greedy), matching the fewest possible characters.

GreedyLazyRepetition Count
**?0 or more times
++?1 or more times
???0 or 1 time (optional)
{n}{n}?Exactly n times
{n,}{n,}?At least n times
{n,m}{n,m}?Between n and m times (inclusive)

For example, against the string '<em>one</em> and <em>two</em>', the greedy pattern /<em>.*<\/em>/ matches the entire string from the first opening tag to the final closing tag, whereas the lazy pattern /<em>.*?<\/em>/g matches each <em>...</em> element individually.

04 / Character Sets & Shorthands

Character Classes & Unicode Property Escapes

Character classes match a single character from a defined set:

  • [abc] and Ranges [a-z0-9]: Matches any single character listed or falling within the ASCII code point range.
  • Negated Class [^abc]: Matches any single character not listed inside the brackets (including newlines).
  • \d / \D: Digit ([0-9]) vs non-digit ([^0-9]).
  • \w / \W: ASCII word character ([A-Za-z0-9_]) vs non-word character.
  • \s / \S: Whitespace (spaces, tabs, line breaks, Unicode spaces) vs non-whitespace.
  • Unicode Property Escapes \p{...} and \P{...} (requires u or v flag): Matches characters by Unicode General Category or Script, such as /\p{Letter}+/gu (matching accented European, Cyrillic, CJK, and Arabic letters that \w misses) or /\p{Script=Greek}+/gu.
05 / Sub-Patterns & Context Assertions

Capturing Groups, Backreferences & Lookarounds

Grouping and lookaround assertions allow you to extract structured fields and validate surrounding context without consuming characters:

SyntaxConstructBehaviour
(pattern)Numbered Capturing GroupStores match in result[1..n]; backreferenced via \1.
(?<name>pattern)Named Capturing GroupStores match in result.groups.name; backreferenced via \k<name>.
(?:pattern)Non-Capturing GroupGroups expression for quantifiers/alternation without capturing.
(?=pattern)Positive LookaheadAsserts that what immediately follows matches pattern.
(?!pattern)Negative LookaheadAsserts that what immediately follows does not match pattern.
(?<=pattern)Positive LookbehindAsserts that what immediately precedes matches pattern.
(?<!pattern)Negative LookbehindAsserts that what immediately precedes does not match pattern.

You can inspect numbered groups, named groups, and group character offsets interactively in our JavaScript Regex Tester.

06 / Compiler Modifiers

JavaScript Regular Expression Flags

Flags are appended after the closing slash (/pattern/flags) or passed as the second argument to new RegExp(pattern, flags):

  • g (global): Retains lastIndex across calls and finds all matches.
  • i (ignoreCase): Performs case-insensitive matching.
  • m (multiline): Makes ^ and $ match line boundaries.
  • s (dotAll): Allows . to match newline characters (\n, \r).
  • u (unicode): Enables strict Unicode code-point mode and \p{...} escapes.
  • v (unicodeSets): Enables set notation, string properties, and deep case-folding inside classes.
  • y (sticky): Requires matches to start strictly at regex.lastIndex.
  • d (hasIndices): Populates match.indices with start and end offsets for every capture group.
07 / Runtime API Reference

JavaScript RegExp & String Code Examples

JavaScript splits regular expression methods across RegExp.prototype (test, exec) and String.prototype (match, matchAll, replace, replaceAll, search, split). Choosing the right method avoids unnecessary allocations:

Choosing the Right JavaScript Regex Method (javascript)
// 1. Boolean check only -> Use RegExp.prototype.test() (Fastest, no array allocation)
const isHexColor = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test("#ff6b2b"); // true

// 2. Extract all matches WITH capture groups -> Use String.prototype.matchAll()
const markdown = "Visit [Regex Tester](/tools/regex-tester) and [Aphex Archive](/resources/aphex-creations).";
const linkRegex = /\[(?<label>[^\]]+)\]\((?<href>[^)]+)\)/g;

for (const match of markdown.matchAll(linkRegex)) {
  console.log(match.groups.label, "->", match.groups.href);
}

// 3. Transform matched tokens dynamically -> Use String.prototype.replace() with callback
const redactEmails = (input) =>
  input.replace(/\b([a-z0-9._%+-]+)@([a-z0-9.-]+\.[a-z]{2,})\b/gi, (_full, user, domain) => {
    return `${user[0]}***@${domain}`;
  });

// 4. Split on variable whitespace or punctuation -> Use String.prototype.split()
const tokens = "html, css ;  javascript |  regex".split(/\s*[,;|]\s*/);
// ["html", "css", "javascript", "regex"]
08 / Reliability & Security

Common Mistakes & Performance Considerations (ReDoS)

Even experienced JavaScript engineers encounter three subtle regular expression pitfalls in production systems:

1. Stateful lastIndex Bugs with Global (/g) and Sticky (/y) Regexes

When a RegExp instance has the g or y flag set, calling regex.test(str) or regex.exec(str) mutates regex.lastIndex. Reusing a module-scoped /pattern/g with .test() across multiple requests or React renders causes alternating true / false return values! Omit the g flag when you only call .test(), or use str.matchAll(regex) which clones the regular expression internally.

2. Catastrophic Backtracking & Regular Expression Denial of Service (ReDoS)

JavaScript’s NFA regex engine uses backtracking. Nesting quantifiers over overlapping patterns—such as /(a+)+$/, /([a-zA-Z0-9]+)*$/, or /(.*a){10}/—creates $O(2^n)$ exponential execution time when given a near-matching string that fails at the final character, freezing the Node.js event loop or browser main thread.

Avoiding Catastrophic Backtracking (ReDoS) (javascript)
// DANGEROUS: Nested overlapping quantifiers (+ inside +)
// Evaluates in O(2^n) time on "aaaaaaaaaaaaaaaaaaaaaaaaaaaa!"
const vulnerable = /^([a-z0-9]+)+$/;

// SAFE: Flatten the character class and bound input length before regex evaluation
const safeSlug = /^[a-z0-9]{1,120}(?:-[a-z0-9]{1,120})*$/;

3. Unescaped Dots & Missing Start/End Anchors in Validation

Writing /example.com/ matches "my-fake-exampleXcom.evil.org" because . matches any character and the pattern lacks ^ and $ anchors. Always escape literal dots (\.) and anchor validation expressions at both ends (/^example\.com$/i).

Ready to validate and benchmark your expressions? Open our Free JavaScript Regex Tester or explore the historical roots of browser-based regex debugging in the Aphex Creations Archive.

Connected Reference & Utilities