The Complete Guide to Regular Expressions in 2026
A complete guide to regular expressions (regex) in JavaScript — character classes, anchors, quantifiers, groups, alternation, flags, lookaround and the string methods — with practical examples throughout.
A regular expression (regex) is a pattern for matching text — validating input, searching, extracting and replacing. They look cryptic but are built from a small set of pieces. This complete guide teaches regex in JavaScript from character classes to lookaround, with practical examples you can run, plus the pitfalls to avoid.
Creating a regex
JavaScript has two ways to make a regex: a literal between slashes (most common), or the RegExp constructor (when you build the pattern from a string). Flags go after the closing slash or as a second argument.
const re1 = /hello/i; // literal, case-insensitive
const re2 = new RegExp("hello", "i"); // constructor (dynamic patterns)
re1.test("Hello world"); // true
Character classes
Character classes match a *type* of character. The shorthands cover the common cases; square brackets define your own set.
\d— a digit (0–9);\D— a non-digit.\w— a word character (letter, digit, underscore);\W— the opposite.\s— whitespace;\S— non-whitespace..— any character except a newline.[abc]— any one of a, b, c;[a-z]— a range;[^abc]— anything except a, b, c.
/\d\d\d/.test("123"); // true — three digits
/[a-z]+/i.test("Hello"); // true — one or more letters
/[^0-9]/.test("a"); // true — a non-digit
Anchors and boundaries
Anchors match a *position*, not a character. ^ and $ tie the pattern to the start and end; \b matches a word boundary — the edge between a word character and a non-word character.
/^hello/.test("hello world"); // true — starts with "hello"
/world$/.test("hello world"); // true — ends with "world"
/\bcat\b/.test("the cat sat"); // true — whole word "cat"
/\bcat\b/.test("category"); // false — "cat" is part of a word
Quantifiers
Quantifiers say *how many* times the preceding item may repeat. By default they are greedy (match as much as possible); add ? to make them lazy (as little as possible).
*— zero or more;+— one or more;?— zero or one (optional).{3}— exactly 3;{2,4}— 2 to 4;{2,}— 2 or more.- Add
?after any quantifier for lazy matching:+?,*?,{2,4}?.
/\d+/.exec("abc123")[0]; // "123" — one or more digits
/colou?r/.test("color"); // true — the "u" is optional
/".*"/.exec('"a" "b"')[0]; // '"a" "b"' — greedy
/".*?"/.exec('"a" "b"')[0]; // '"a"' — lazy
Groups and capturing
Parentheses group part of a pattern and capture what it matched, so you can extract or reuse it. Use (?:…) for a non-capturing group, and (?<name>…) for a named capture that’s easier to read.
const m = /(\d{4})-(\d{2})-(\d{2})/.exec("2026-07-29");
m[1]; // "2026" m[2]; // "07" m[3]; // "29"
// named groups
const d = /(?<year>\d{4})-(?<month>\d{2})/.exec("2026-07");
d.groups.year; // "2026"
Alternation
The pipe | means "or" — match one alternative or another. Group it with parentheses to bound the choice.
/cat|dog/.test("I have a dog"); // true
/^(yes|no)$/.test("yes"); // true — exactly "yes" or "no"
Flags
Flags change how the whole regex behaves. Combine them (e.g. /…/gi).
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 mode (needed for\p{…}and correct surrogate handling).y— sticky: match only at the exactlastIndexposition.
"a1 b2 c3".match(/\w\d/g); // ["a1", "b2", "c3"] — global
Lookahead and lookbehind
Lookaround asserts what comes before or after a position without consuming it. Useful for "match X only if followed/preceded by Y" — like validating a password has a digit, without including the digit in the match.
/\d+(?= dollars)/.exec("50 dollars")[0]; // "50" — positive lookahead
/(?<=\$)\d+/.exec("$50")[0]; // "50" — positive lookbehind
/foo(?!bar)/.test("foobaz"); // true — negative lookahead
Using regex in JavaScript
Regex plugs into several string and RegExp methods:
regex.test(str)— does it match? Returns a boolean.str.match(regex)— the first match, or all matches with thegflag.str.matchAll(regex)— an iterator of all matches with capture groups.str.replace(regex, replacement)— replace matches; use$1or$<name>for groups.str.split(regex)— split on a pattern.
"2026-07-29".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"); // "29/07/2026"
for (const m of "a1 b2".matchAll(/(\w)(\d)/g)) console.log(m[1], m[2]);
Pitfalls and best practices
- Don’t over-validate with regex. Email/URL validation is notoriously hard to get right — a loose check plus real verification beats a monstrous pattern. For email, a simple
/^\S+@\S+\.\S+$/plus sending a confirmation is more reliable than a "perfect" regex. - Watch catastrophic backtracking. Nested quantifiers on overlapping patterns (like
(a+)+) can hang on certain inputs (a ReDoS risk). Keep patterns simple and avoid ambiguous nesting. - Escape special characters when matching literals:
.,*,+,?,(,),[,],{,},|,\,^,$all have meaning. UseRegExp.escape()(ES2025) or a backslash. - Comment complex patterns — a regex you can’t read is a bug waiting to happen. Break it up or add a comment explaining intent.
Regex cheat sheet
\d \w \s— digit, word char, whitespace (uppercase = negation).^ $ \b— start, end, word boundary.* + ?and{n,m}— quantifiers (add?for lazy).( )capture,(?: )non-capturing,(?<name> )named.|alternation;[ ]character set;.any char.- Flags:
gglobal,iignore-case,mmultiline,sdotall,uunicode. (?= ) (?! ) (?<= ) (?<! )— look-ahead/behind (positive/negative).
Frequently asked questions
What is a regular expression?
\d for digits, quantifiers like + for "one or more", anchors like ^ for the start of a string, and groups. Regex is supported in virtually every programming language and in tools like editors and command-line utilities.What is the difference between greedy and lazy matching?
? after a quantifier makes it lazy, matching as little as possible. For example, against "a" "b", the pattern ".*" (greedy) matches the whole "a" "b", while ".*?" (lazy) matches just "a". Lazy matching is useful when you want the shortest match between delimiters.How do I validate an email with regex?
/^\S+@\S+\.\S+$/ to catch obvious mistakes, then verify the address for real by sending a confirmation email. That combination is far more reliable than any single "perfect" email regex.What does the g flag do in regex?
g (global) flag makes the regex find all matches in a string rather than stopping at the first. With String.match, adding g returns an array of every match; without it you get just the first match plus capture groups. The g flag is also required by matchAll and by replace/replaceAll when you want to replace every occurrence, not just the first.Test regular expressions live in XCODX Studio — run JavaScript in the browser and iterate on patterns instantly. See also the complete guide to JavaScript async and JavaScript best practices.