DEVESSENTIALS

Regex Cheat Sheet for JavaScript Developers

Regular expressions are one of the most powerful — and most forgotten — tools in a developer's toolkit. This cheat sheet covers everything you need: syntax, flags, JavaScript-specific methods, named groups, lookaheads, and the most common ready-to-use patterns.

Creating a Regex in JavaScript

There are two ways to create a regular expression in JavaScript:

// Literal syntax — use this when the pattern is known at write time
const re = /pattern/flags;

// Constructor syntax — use this when the pattern is dynamic
const re = new RegExp('pattern', 'flags');
const re = new RegExp(userInput, 'i'); // ⚠ sanitize userInput first

// Examples
const emailRe = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i;
const dynRe = new RegExp('^' + prefix + '\\d+$');

Anchors

TokenMatchesExample
^Start of string (or line with m flag)/^hello/ matches 'hello world'
$End of string (or line with m flag)/world$/ matches 'hello world'
\bWord boundary/\bcat\b/ matches 'cat' but not 'cats'
\BNon-word boundary/\Bcat\B/ matches 'locate' but not 'cat'

Character Classes

TokenMatches
.Any character except newline (use s flag to include newline)
\dDigit: [0-9]
\DNon-digit: [^0-9]
\wWord character: [a-zA-Z0-9_]
\WNon-word character
\sWhitespace: space, tab, newline, carriage return
\SNon-whitespace
[abc]Any of a, b, or c
[^abc]Any character except a, b, or c
[a-z]Any lowercase letter a through z
[a-zA-Z0-9]Alphanumeric characters

Quantifiers

TokenMeaningLazy version
*0 or more (greedy)*?
+1 or more (greedy)+?
?0 or 1 (greedy)??
{n}Exactly n times{n}? (same)
{n,}n or more times{n,}?
{n,m}Between n and m times{n,m}?

Greedy vs lazy: By default quantifiers are greedy — they match as much as possible. Add ? after any quantifier to make it lazy (match as little as possible).

const html = '<b>bold</b><i>italic</i>';

// Greedy — matches from first < to last >
html.match(/<.+>/)[0];   // '<b>bold</b><i>italic</i>'

// Lazy — matches the shortest possible
html.match(/<.+?>/)[0];  // '<b>'

Groups and Alternation

SyntaxDescription
(abc)Capturing group — captured as $1, $2... or match[1], match[2]...
(?<name>abc)Named capturing group — accessed via match.groups.name (ES2018+)
(?:abc)Non-capturing group — groups without capturing (better performance)
a|bAlternation — matches a or b
(cat|dog)sMatches 'cats' or 'dogs'
// Numbered capture groups
const date = '2026-04-09';
const m = date.match(/(\d{4})-(\d{2})-(\d{2})/);
// m[1] = '2026', m[2] = '04', m[3] = '09'

// Named capture groups (ES2018+)
const m2 = date.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
// m2.groups.year = '2026', m2.groups.month = '04', m2.groups.day = '09'

// Non-capturing group — groups without creating a backreference
const re = /(?:https?|ftp):\/\//;  // matches http:// or https:// or ftp://

Lookahead and Lookbehind

Lookarounds match a position without consuming characters. The text inside the lookaround is not included in the match result.

SyntaxTypeExample
(?=pattern)Positive lookahead — X followed by pattern/\d+(?= dollars)/ matches '100' in '100 dollars'
(?!pattern)Negative lookahead — X NOT followed by pattern/\d+(?! dollars)/ matches '100' in '100 euros'
(?<=pattern)Positive lookbehind — X preceded by pattern (ES2018+)/(?<=\$)\d+/ matches '100' in '$100'
(?<!pattern)Negative lookbehind — X NOT preceded by pattern (ES2018+)/(?<!\$)\d+/ matches '100' in '100px' but not '$100'

Flags

FlagNameEffect
gGlobalFind all matches, not just the first. Makes exec()/test() stateful via lastIndex.
iCase-insensitive/abc/i matches 'ABC', 'Abc', 'abc'
mMultilineMakes ^ and $ match start/end of each line, not just the string
sDotallMakes . match newline characters too (ES2018+)
uUnicodeEnables full Unicode matching, required for surrogate pairs and \p{} syntax
vUnicode setsEnhanced Unicode mode with set operations in character classes (ES2024+)
dIndicesAdds .indices property to match result with start/end positions (ES2022+)

JavaScript String and RegExp Methods

// ---- String methods that take a regex ----

// test: returns true/false
/^\d+$/.test('123');         // true
/^\d+$/.test('12a');         // false

// match: returns first match (without g) or all matches (with g)
'hello world'.match(/\w+/);  // ['hello', index: 0, ...]
'hello world'.match(/\w+/g); // ['hello', 'world']

// matchAll: returns iterator of all matches with groups (requires g flag)
const matches = [...'2026-04-09'.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)];
matches[0][1]; // '2026'

// replace: replace first match (without g) or all (with g)
'foo bar foo'.replace(/foo/, 'baz');   // 'baz bar foo'
'foo bar foo'.replace(/foo/g, 'baz'); // 'baz bar baz'

// Replace with function — args: (match, group1, group2..., offset, string)
'hello world'.replace(/(\w+)/g, (match) => match.toUpperCase());
// 'HELLO WORLD'

// replaceAll: ES2021+, no g flag needed
'foo bar foo'.replaceAll('foo', 'baz'); // 'baz bar baz'
'foo bar foo'.replaceAll(/foo/g, 'baz'); // still needs g flag with regex

// search: returns index of first match or -1
'hello world'.search(/world/); // 6
'hello world'.search(/xyz/);   // -1

// split: split string using regex as delimiter
'one1two2three'.split(/\d/);  // ['one', 'two', 'three']

// ---- RegExp methods ----

// exec: returns match result or null, advances lastIndex when global
const re = /\d+/g;
const str = 'abc 123 def 456';
let m;
while ((m = re.exec(str)) !== null) {
  console.log(`Found ${m[0]} at index ${m.index}`);
}
// Found 123 at index 4
// Found 456 at index 12

Common Regex Patterns

PatternRegex
Integer (positive)/^\d+$/
Decimal number/^-?\d+(\.\d+)?$/
Email (basic)/^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i
URL (http/https)/^https?:\/\/[^\s/$.?#].[^\s]*$/i
IPv4 address/^(\d{1,3}\.){3}\d{1,3}$/
Date (YYYY-MM-DD)/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/
Hex color/^#([0-9a-f]{3}|[0-9a-f]{6})$/i
UUID v4/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
Slug (URL-friendly)/^[a-z0-9]+(?:-[a-z0-9]+)*$/
Whitespace only/^\s*$/
HTML tag/<([a-z][a-z0-9]*)(?:\s[^>]*)?>(.*?)<\/\1>/is
Credit card (basic)/^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/

Note on email validation: The regex above catches most invalid emails but the full email spec (RFC 5321) is far more complex. For production use, send a confirmation email rather than relying on regex alone.

Backreferences

You can refer to a previously captured group within the same regex using \1, \2, etc., or by name with \k<name>.

// Match duplicate words
/\b(\w+)\s+\1\b/.test('the the');  // true — \1 refers to group 1

// Named backreference
/(?<word>\w+)\s+\k<word>/.test('hello hello');  // true

// Match HTML open/close tag pairs
/<(\w+)>.*?<\/\1>/s.test('<b>text</b>');  // true
/<(\w+)>.*?<\/\1>/s.test('<b>text</i>');  // false

Special Characters That Need Escaping

These characters have special meaning in regex and must be escaped with \ to match them literally:

. * + ? ^ $ { } [ ] | ( ) \

// To match a literal dot:
/\./.test('3.14');  // true (matches the dot)
/./.test('3.14');   // true (matches any character — not what you want)

// Never pass unsanitized user input directly to RegExp constructor:
// const re = new RegExp(userInput);  ← dangerous if input contains *, +, |, etc.

// Use the escape-string-regexp package (npm install escape-string-regexp):
import escapeStringRegexp from 'escape-string-regexp';
const userInput = 'hello.world';
const re = new RegExp(escapeStringRegexp(userInput));  // safe

Want to test a regex pattern interactively? Open the Regex Tester →

Frequently Asked Questions

What is the difference between test() and match() in JavaScript regex?

test() is called on the RegExp object and returns a boolean — it just tells you whether the pattern matches. match() is called on a string and returns the match result (the matched text, capture groups, index) or null. Use test() when you only need to know if something matches. Use match() or exec() when you need the actual matched content or capture group values.

Why does my regex with the g flag behave differently on repeated calls?

RegExp objects with the global (g) flag are stateful — they remember the position of the last match via the lastIndex property. Each call to exec() or test() starts from lastIndex, not from the beginning. This causes surprising behavior in loops. To avoid this, either create a new RegExp on each call, use matchAll() which handles this correctly, or manually reset lastIndex to 0 before reusing the regex.

What is the difference between greedy and lazy quantifiers?

Greedy quantifiers (*, +, ?) match as much as possible. Lazy quantifiers (*?, +?, ??) match as little as possible. Example: with the string '<b>bold</b><i>italic</i>', the pattern <.+> (greedy) matches the entire string from first < to last >. The pattern <.+?> (lazy) matches just '<b>'.

When should I use named capture groups instead of numbered groups?

Use named capture groups ((?<name>...)) whenever the regex has more than 2 groups, or when the meaning of each group isn't obvious from its position. Named groups make the code self-documenting and resilient to adding new groups — numbered groups shift when you insert a new group before them. Access named groups via match.groups.name in JavaScript (ES2018+).

How do I match a literal dot, parenthesis, or other special character?

Escape it with a backslash. In regex, these characters have special meaning: . * + ? ^ $ { } [ ] | ( ) \. To match them literally, prefix with \: \. matches a literal dot, \( matches a literal opening parenthesis, \+ matches a literal plus sign. In a JavaScript string passed to new RegExp(), you need to double-escape: new RegExp('\\d+') is the equivalent of /\d+/.