artisan.dev

JavaScript Regex Cheatsheet: Syntax, Flags and Common Pitfalls

By Tahsin Abrar · Updated

Regular expressions are a small language of their own, and JavaScript's flavour has grown a lot in recent years: named groups, lookbehind, Unicode property escapes and the d and v flags are all available in current browsers and Node.js. This cheatsheet collects the syntax you reach for most, with examples you can paste into the Regex Tester to see matches highlighted live.

Try itOpen the Regex Tester — runs in your browser

Characters and classes

PatternMatches
.Any character except line terminators (add the s flag to include them)
\d / \DA digit 0–9 / anything else
\w / \WA word character [A-Za-z0-9_] / anything else
\s / \SWhitespace (space, tab, newline, and Unicode spaces) / anything else
[abc]One of a, b or c
[^abc]Any character except a, b or c
[a-z0-9]A character in either range
\p{L}Any Unicode letter (requires the u or v flag)
\. \* \?A literal ., * or ? — escape any of ^$\.*+?()[]{}|/

Anchors and boundaries

PatternMatches
^ / $Start / end of the input — or of each line with the m flag
\bA word boundary, e.g. \bcat\b matches "cat" but not "concatenate"
\BA position that is not a word boundary

Quantifiers

PatternMeaning
*0 or more
+1 or more
?0 or 1 (optional)
{3}Exactly 3
{2,}2 or more
{2,5}Between 2 and 5
*? +? ??Lazy versions — match as few as possible

Quantifiers are greedy by default. On the input <b>bold</b>, the pattern <.+> matches the whole string, while the lazy <.+?> matches just <b>.

Groups, backreferences and lookarounds

PatternMeaning
(abc)Capturing group, referenced as $1 in replacements
(?<year>\d{4})Named group, available as match.groups.year and $<year>
(?:abc)Non-capturing group — groups without capturing
a|bAlternation: a or b
\1 / \k<year>Backreference to an earlier group, e.g. (\w)\1 finds doubled letters
x(?=y)Lookahead: x only if followed by y
x(?!y)Negative lookahead: x only if not followed by y
(?<=y)xLookbehind: x only if preceded by y
(?<!y)xNegative lookbehind: x only if not preceded by y
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { year, month } = "2026-10-09".match(re).groups;

"2026-10-09".replace(re, "$<day>/$<month>/$<year>"); // "09/10/2026"

Flags

FlagEffect
gGlobal — find all matches, not just the first
iCase-insensitive
mMultiline — ^ and $ match at line breaks
sdotAll — . also matches newlines
uUnicode — treats surrogate pairs as one character and enables \p{…}
ySticky — match only at lastIndex
dIndices — adds start/end positions for each group in match.indices
vUnicodeSets — an upgraded u with set operations like [\p{L}--[a-z]]

Practical patterns

TaskPattern
Trim whitespace/^\s+|\s+$/g
Collapse repeated spaces/ {2,}/g
Integer or decimal/^-?\d+(\.\d+)?$/
Hex colour/^#(?:[0-9a-f]{3}){1,2}$/i
ISO date (shape only)/^\d{4}-\d{2}-\d{2}$/
Simple email sanity check/^[^\s@]+@[^\s@]+\.[^\s@]+$/
UUID/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
Slug/^[a-z0-9]+(?:-[a-z0-9]+)*$/

A regex can check that an email looks plausible, but no practical pattern validates real addresses. Send a confirmation email instead. Likewise, validate dates with a date library — \d{4}-\d{2}-\d{2} happily accepts 2026-13-45.

Pitfalls that catch everyone

test() with the g flag remembers its position

A regex with g or y stores lastIndex between calls. Reusing it with test() alternates between true and false on the same input:

const re = /a/g;
re.test("a"); // true  (lastIndex is now 1)
re.test("a"); // false (search starts at 1, resets to 0)

Drop the g flag when you only need a yes/no answer, or reset re.lastIndex = 0.

Double escaping in the RegExp constructor

In a string, \ is itself an escape character, so new RegExp("\d+") produces the pattern d+. Write new RegExp("\\d+"), or use a literal /\d+/ when the pattern is fixed. When building a pattern from user input, escape it first with s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").

Catastrophic backtracking

Nested quantifiers such as (a+)+$ or (\w+\s?)*$ can take exponential time on inputs that almost match, freezing a browser tab or a Node.js server (a "ReDoS"). Avoid quantifying a group that itself contains an overlapping quantifier, anchor patterns where you can, and cap input length before matching untrusted text.

matchAll and replaceAll need the g flag

str.matchAll(re) and str.replaceAll(re, …) throw a TypeError if re is a regex without g. Use match/replace for a single match.

FAQ

Does JavaScript support lookbehind?
Yes. Positive (?<=…) and negative (?<!…) lookbehind work in all current browsers and Node.js; Safari added support in version 16.4.
Why doesn't . match a newline?
By default . excludes line terminators. Add the s (dotAll) flag, or use [\s\S] to match any character including newlines.
What is the difference between the u and v flags?
Both enable Unicode-aware matching. v (UnicodeSets) is the newer superset: it adds set subtraction and intersection inside character classes and string properties, and is stricter about escaping in classes.

More guides