On this page
Regex looks like noise until it clicks. Once it does, you’ll find yourself reaching for it for tasks where you’d previously written three loops and a conditional. This is a practical reference for the patterns you’ll actually use in JavaScript — not a grammar spec, just the stuff that comes up.
Test any of these patterns interactively.
The two ways to write a regex in JavaScript
// Literal syntax (use this most of the time)
const pattern = /hello/;
// Constructor (use when the pattern is dynamic)
const pattern = new RegExp(variable);
The literal syntax is faster and lets your editor highlight the pattern. Use the constructor only when you need to build the pattern from a string at runtime.
Character classes
| Shorthand | What it matches |
|---|---|
\d | Any digit (0-9) |
\w | Word character (a-z, A-Z, 0-9, underscore) |
\s | Whitespace (space, tab, newline) |
\D | Not a digit |
\W | Not a word character |
\S | Not whitespace |
. | Any character except newline |
Custom sets use square brackets: [aeiou] matches any vowel, [a-z] matches any lowercase letter, [^0-9] matches anything that isn’t a digit (the caret inside brackets negates the set).
Quantifiers
| Symbol | Meaning |
|---|---|
* | 0 or more |
+ | 1 or more |
? | 0 or 1 (makes the previous item optional) |
{n} | Exactly n times |
{n,m} | Between n and m times |
Quantifiers are greedy by default — they match as much as possible. Add ? to make them lazy (.*? matches as little as possible). This matters when you’re matching between two delimiters and don’t want it to eat everything up to the last one.
Anchors
^ matches the start of a string. $ matches the end. Put them together to assert an exact match:
/^hello$/.test('hello') // true
/^hello$/.test('hello world') // false
\b is a word boundary — useful for matching whole words without anchoring to start/end of string:
/\bcat\b/.test('the cat sat') // true
/\bcat\b/.test('concatenate') // false
Flags
Flags go after the closing slash and change how matching works:
| Flag | Effect |
|---|---|
g | Global: find all matches, not just the first |
i | Case-insensitive |
m | Multiline: ^ and $ match start/end of each line |
s | DotAll: makes . match newlines too |
'Hello hello HELLO'.match(/hello/gi) // ['Hello', 'hello', 'HELLO']
Validation patterns that work in practice
Email (good enough for almost all use cases):
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
Not RFC-5322 compliant, but covers everything that looks like an email without false negatives. Full RFC validation is surprisingly complex and usually not worth it.
URL (covers http and https):
/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/
Hex color (with or without the #):
/#?([a-f0-9]{6}|[a-f0-9]{3})/i
Password strength (at least 8 chars, one uppercase, one digit):
/^(?=.*[A-Z])(?=.*\d).{8,}$/
The (?=...) parts are positive lookaheads. They assert that something must exist at the current position without consuming characters. So this pattern says: “from the start, there must be at least one uppercase somewhere ahead, at least one digit somewhere ahead, and the total length must be 8 or more.”
URL-friendly slug:
/^[a-z0-9]+(?:-[a-z0-9]+)*$/
Replace with capture groups
.replace() accepts regex and lets you use capture groups in the replacement string via $1, $2, etc.:
// Swap first and last name
'Smith, John'.replace(/(\w+), (\w+)/, '$2 $1')
// 'John Smith'
// Convert camelCase to kebab-case
'camelCase'.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)
// 'camel-case'
When you pass a function as the replacement, it receives the matched text (and capture groups as additional arguments) and returns the replacement string.
Extracting matches
.match() with the g flag returns an array of all matches:
'Total: $12.50 and $7.99'.match(/\$[\d.]+/g)
// ['$12.50', '$7.99']
Without g, it returns the first match plus capture groups. .matchAll() (ES2020) returns an iterator of all matches including capture groups for each.
Testing before shipping
Edge cases are where regex patterns fail. The phone number that has parentheses. The email with a plus sign. The URL with a fragment identifier. Testing a pattern against five or six representative inputs before using it in production saves debugging time later.