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.
Characters and classes
| Pattern | Matches |
|---|---|
. | Any character except line terminators (add the s flag to include them) |
\d / \D | A digit 0–9 / anything else |
\w / \W | A word character [A-Za-z0-9_] / anything else |
\s / \S | Whitespace (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
| Pattern | Matches |
|---|---|
^ / $ | Start / end of the input — or of each line with the m flag |
\b | A word boundary, e.g. \bcat\b matches "cat" but not "concatenate" |
\B | A position that is not a word boundary |
Quantifiers
| Pattern | Meaning |
|---|---|
* | 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
| Pattern | Meaning |
|---|---|
(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|b | Alternation: 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)x | Lookbehind: x only if preceded by y |
(?<!y)x | Negative 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
| Flag | Effect |
|---|---|
g | Global — find all matches, not just the first |
i | Case-insensitive |
m | Multiline — ^ and $ match at line breaks |
s | dotAll — . also matches newlines |
u | Unicode — treats surrogate pairs as one character and enables \p{…} |
y | Sticky — match only at lastIndex |
d | Indices — adds start/end positions for each group in match.indices |
v | UnicodeSets — an upgraded u with set operations like [\p{L}--[a-z]] |
Practical patterns
| Task | Pattern |
|---|---|
| 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 thes(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
- How JWT Signatures Work (HS256 vs RS256, Explained)What the three parts of a JSON Web Token are, how the signature is computed, why decoding is not verifying, and the mistakes that lead to JWT vulnerabilities.
- Cron Syntax Cheatsheet: Fields, Operators and 25 Ready-Made SchedulesA practical reference to the five-field cron format, the special characters, common schedules you can copy, and the gotchas in crontab, GitHub Actions and Kubernetes.
- Fixing "Unexpected token" and Other JSON Parse ErrorsWhat JSON.parse errors like "Unexpected token < in JSON", "Unexpected end of JSON input" and "Expected double-quoted property name" really mean, and how to fix each one.