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.
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.
// 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, "\\$&")).
Anchors & Word Boundaries
Anchors do not match characters; instead, they assert a specific cursor boundary before, after, or between characters:
| Anchor | Meaning | Example |
|---|---|---|
^ | 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$/ |
\b | Word boundary (transition between \w and \W or start/end) | /\bcat\b/ (matches "cat", not "category") |
\B | Non-word boundary (cursor is between two word or two non-word chars) | /\Bscript\b/ (matches "JavaScript") |
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.
| Greedy | Lazy | Repetition 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.
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{...}(requiresuorvflag): Matches characters by Unicode General Category or Script, such as/\p{Letter}+/gu(matching accented European, Cyrillic, CJK, and Arabic letters that\wmisses) or/\p{Script=Greek}+/gu.
Capturing Groups, Backreferences & Lookarounds
Grouping and lookaround assertions allow you to extract structured fields and validate surrounding context without consuming characters:
| Syntax | Construct | Behaviour |
|---|---|---|
(pattern) | Numbered Capturing Group | Stores match in result[1..n]; backreferenced via \1. |
(?<name>pattern) | Named Capturing Group | Stores match in result.groups.name; backreferenced via \k<name>. |
(?:pattern) | Non-Capturing Group | Groups expression for quantifiers/alternation without capturing. |
(?=pattern) | Positive Lookahead | Asserts that what immediately follows matches pattern. |
(?!pattern) | Negative Lookahead | Asserts that what immediately follows does not match pattern. |
(?<=pattern) | Positive Lookbehind | Asserts that what immediately precedes matches pattern. |
(?<!pattern) | Negative Lookbehind | Asserts 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.
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): RetainslastIndexacross 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 atregex.lastIndex.d(hasIndices): Populatesmatch.indiceswith start and end offsets for every capture group.
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:
// 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"]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.
// 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.