A Regex Is a Pattern for Matching Text
Regular expressions describe shapes of strings: "four digits," "a word followed by a comma," "anything that looks like an email." JavaScript gives you two ways to create one:
The literal form — slashes around the pattern, flags after the closing slash — is what you'll use most of the time. Reach for new RegExp(...) when the pattern itself is dynamic, like building it from user input or a variable.
The i on the end is a flag. i means case-insensitive. More on flags in a moment.
test: Does It Match?
The quickest thing you can ask a regex is "does this string contain a match?" That's test:
\d means "any digit." test returns true or false and nothing else. If all you need is a yes-or-no answer — validating a field, filtering an array — test is the right tool.
match: Pull the Matched Text Out
When you want the matched text itself, use the string's match method:
Without the g flag, match returns an array with the first match plus metadata (index, input). With g, it returns every match as a plain array of strings. If nothing matches, you get null — not an empty array — so guard accordingly:
Flags Change How the Pattern Behaves
Flags go after the closing slash and tweak how matching works. The ones you'll reach for most:
g— global, find every match instead of just the first.i— case-insensitive.m— multiline, so^and$match line starts/ends, not just string start/end.s— dotall, so.also matches newlines.u— unicode-aware, needed for many emoji and non-ASCII patterns.
Without m, ^ only anchors to the very start of the string. With m, it anchors to the start of each line, so both Roses and Violets get picked up.
Character Classes and Quantifiers
The building blocks of most patterns:
\ddigit,\wword character (letter/digit/underscore),\swhitespace.[abc]one of a, b, or c.[^abc]anything except those.[a-z]a range..any character except newline.*zero or more,+one or more,?zero or one.{3}exactly three,{2,5}between two and five,{2,}two or more.^start,$end.
Put together:
\b is a word boundary — the invisible line between a word character and a non-word character. Useful when you want "whole word" matches.
Capture Groups: Remembering Parts of a Match
Parentheses create a group that captures whatever it matched. exec and match return those captures alongside the overall match:
Index 0 is the full match, and each group gets its own slot after that. Counting positional groups gets awkward once you have more than two, so name them:
Named groups read better at the call site and survive reordering the pattern.
replace: Rewrite Matched Text
replace takes a pattern and a replacement. The replacement can be a string or a function:
Without g, only the first match gets replaced. A common mistake is forgetting the flag and wondering why the second email still looks wrong.
Replacement strings support back-references. $1, $2, etc. refer to capture groups; $<name> refers to named ones:
For anything beyond a straight substitution, pass a function. It receives the match and the captures as arguments:
The underscore is the full match (which we don't need); n is the first capture group. This pattern — regex plus replacer function — handles most real-world text munging.
matchAll: Every Match With Its Groups
String.prototype.matchAll returns an iterator of every match with its capture groups — something plain match with the g flag can't do:
matchAll requires the g flag. Without it, you'll get a TypeError. Spread it into an array ([...text.matchAll(email)]) if you need random access rather than iteration.
Escaping Special Characters
Characters like . * + ? ( ) [ ] { } | \ ^ $ have special meaning in a regex. To match them literally, escape with a backslash:
The unescaped version matches examplexcom because . means "any character." This kind of bug is common — and silent. If a regex is matching too much, look for an un-escaped . first.
When building a pattern from user input, you must escape it, or a user can inject regex syntax:
$& in a replacement string is shorthand for "the whole match."
Lookahead and Lookbehind
Sometimes you want to match text only when it's followed (or preceded) by something — without including that something in the match. That's what lookarounds do:
(?= ...)positive lookahead: "followed by."(?<= ...)positive lookbehind: "preceded by."(?! ...)and(?<! ...)are the negative versions.
Lookarounds don't consume characters, so the part you're "looking at" stays available for the next part of the pattern.
A Word on Email Validation
You'll see this come up a lot: "give me a regex that validates emails." The honest answer is: don't. Real email grammar is gnarly, and any regex short enough to read is wrong in some direction. For form validation, a pragmatic pattern is fine:
Read it as: "some non-whitespace, non-@ characters, an @, more of the same, a dot, more of the same." That catches obvious typos without pretending to enforce RFC 5322. For real verification, send a confirmation email.
Common Pitfalls
A few traps worth internalising:
- Forgetting
gwithreplaceormatchAll. First match only, or aTypeError. - Stateful
lastIndexon global regexes. A regex with thegoryflag remembers where it left off betweentest/execcalls. Don't reuse one across unrelated strings — create a fresh one, or usematchAll. - Unescaped dots and slashes in dynamic patterns. Always escape user input before shoving it into
new RegExp(...). - Catastrophic backtracking. Nested quantifiers like
(a+)+on pathological input can freeze a tab. If a regex feels slow, simplify.
Next: Dates and Times
Regex handles the shape of text; real data also comes with timestamps that need parsing, formatting, and arithmetic. The next page covers Date, Intl.DateTimeFormat, and the mental model that keeps timezone bugs away.
Frequently Asked Questions
How do you create a regex in JavaScript?
Two ways. The literal form uses slashes: /hello/i. The constructor takes a string: new RegExp('hello', 'i'). Use the literal when the pattern is fixed, and the constructor when you need to build the pattern from a variable at runtime.
What's the difference between test, match, and exec in JavaScript regex?
regex.test(str) returns a boolean — fastest when you only care whether a match exists. str.match(regex) returns an array of matches (or null). regex.exec(str) returns one match at a time with capture groups and, with the g flag, tracks position across calls via lastIndex.
How do I replace all occurrences with regex in JavaScript?
Use the g flag: str.replace(/foo/g, 'bar'). Without g, only the first match is replaced. You can also call str.replaceAll(/foo/g, 'bar') — but replaceAll with a regex argument requires the g flag, otherwise it throws.
What are capture groups in JavaScript regex?
Parentheses in a pattern create capture groups that remember what they matched. /(\d{4})-(\d{2})/.exec('2024-11') returns an array where index 1 is '2024' and index 2 is '11'. Name them with (?<year>\d{4}) and access via match.groups.year.