JavaScript Regular Expressions: Complete Guide

Regular expressions match patterns in text. Learn JavaScript regex syntax, the test and exec methods, flags, character classes, quantifiers, and practical patterns for validation and search.

8 min read

A regular expression, or regex, is a pattern that describes a set of strings. You use it to test whether a string matches a shape, extract parts of a string, or replace text that fits a pattern.

In JavaScript, you write a regex between forward slashes:

javascriptjavascript
const pattern = /hello/;
 
console.log(pattern.test("hello world")); // true
console.log(pattern.test("goodbye"));     // false

The pattern /hello/ matches any string that contains the characters "hello" in order. test() returns true if the pattern is found anywhere in the string.

Two Ways to Create a Regex

JavaScript offers two syntaxes for building a regex, and picking the right one depends on whether the pattern is known ahead of time or built from a variable at runtime:

javascriptjavascript
// Literal: compiled once, best for static patterns
const literal = /pattern/flags;
 
// Constructor: built at runtime, use for dynamic patterns
const dynamic = new RegExp("pattern", "flags");

Use the literal form unless you need to build the pattern string dynamically. The literal is shorter, more readable, and the engine can optimize it better.

The Core Methods

MethodBelongs toReturnsUse for
test(str)RegExptrue or falseChecking if a pattern exists
exec(str)RegExpMatch array or nullGetting match details, capture groups, and position
match(regex)StringMatch array or nullGetting all matches (with the g flag) or the first match
replace(regex, replacement)StringNew stringReplacing matched text
search(regex)StringIndex or -1Finding the position of a match
split(regex)StringArraySplitting a string by a pattern

test() is the workhorse for simple validation checks. exec() gives you detailed match information when you need it. replace() is for find-and-replace operations on a string:

javascriptjavascript
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 
console.log(email.test("alex@example.com")); // true
console.log(email.test("not-an-email"));     // false
 
const sentence = "The price is 42 dollars";
console.log(sentence.replace(/\d+/, "XX"));  // "The price is XX dollars"

Flags -- Controlling How the Engine Searches

Flags go after the closing slash and change the regex engine's behavior:

FlagNameEffect
gGlobalFind all matches, not just the first
iCase-insensitivea matches A
mMultiline^ and $ match start/end of each line, not just the whole string
sDot-allthe dot matches newline characters too
uUnicodeEnable Unicode features like code point escapes and property escapes
yStickyMatch only from lastIndex, not anywhere in the string
javascriptjavascript
const text = "Apple apple APPLE";
 
console.log(text.match(/apple/));    // ["Apple"] -- first match only
console.log(text.match(/apple/gi));  // ["Apple", "apple", "APPLE"] -- all, case-insensitive

The g flag is especially important. Without it, match() returns only the first match. With it, match() returns every match.

With the global flag, exec() becomes stateful. It remembers where it left off in lastIndex.

Character Classes -- Matching Categories of Characters

Character classes match any single character from a category:

ClassMatchesEquivalent to
.Any character except newline(with the s flag: any character)
\dAny digit[0-9]
\DAny non-digit[^0-9]
\wWord character (letter, digit, underscore)[a-zA-Z0-9_]
\WNon-word character[^a-zA-Z0-9_]
\sWhitespace (space, tab, newline)[ \t\n\r]
\SNon-whitespace[^ \t\n\r]

Custom character classes use square brackets:

javascriptjavascript
const vowels = /[aeiou]/gi;
console.log("Hello World".match(vowels)); // ["e", "o", "o"]
 
const hex = /[0-9a-fA-F]/;   // A single hex digit
const notDigit = /[^0-9]/;    // Any character that is not a digit

Inside square brackets, most special characters lose their meaning. You can use a hyphen for ranges and a caret at the start to negate the class.

Quantifiers -- Controlling Repetition

Quantifiers say how many times the preceding pattern should appear:

QuantifierMeaning
*Zero or more times
+One or more times
?Zero or one time (optional)
{n}Exactly n times
{n,}n or more times
{n,m}Between n and m times
javascriptjavascript
console.log(/a+/.test("aaa"));   // true -- one or more a's
console.log(/a+/.test("bbb"));   // false
console.log(/\d{3}/.test("123")); // true -- exactly three digits
console.log(/\d{3}/.test("12"));  // false
console.log(/\d{2,4}/.test("12345")); // true -- matches first 2-4 digits

By default, quantifiers are greedy: they match as much as possible. Add a question mark after a quantifier to make it lazy, matching as little as possible instead:

javascriptjavascript
const html = "<div>hello</div><div>world</div>";
 
console.log(html.match(/<div>.*<\/div>/)[0]);  // "<div>hello</div><div>world</div>" (greedy)
console.log(html.match(/<div>.*?<\/div>/)[0]); // "<div>hello</div>" (lazy)

Anchors -- Matching Position, Not Characters

Anchors match a position in the string rather than a character:

AnchorMatches
^Start of string (or start of line with the m flag)
$End of string (or end of line with the m flag)
\bWord boundary (between a word character and a non-word character)
\BNon-word boundary
javascriptjavascript
console.log(/^hello/.test("hello world")); // true -- starts with hello
console.log(/^hello/.test("say hello"));   // false
console.log(/world$/.test("hello world")); // true -- ends with world
console.log(/world$/.test("world news"));  // false
 
// Word boundary: match "cat" as a whole word, not inside "category"
console.log(/\bcat\b/.test("a cat sat"));     // true
console.log(/\bcat\b/.test("category"));      // false

Anchors are critical for validation patterns. Without start and end anchors, an email regex would match a string like "not@@@valid@@@email" because it finds a valid-looking substring in the middle.

Common Validation Patterns

Here are practical, tested patterns for common validation tasks:

javascriptjavascript
// Email (simplified, good enough for most form validation)
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 
// US phone number: (123) 456-7890 or 123-456-7890
const phone = /^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
 
// URL
const url = /^https?:\/\/[^\s/$.?#].[^\s]*$/i;
 
// Strong password: 8+ chars, at least one letter and one digit
const password = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/;
 
// Date in YYYY-MM-DD format
const date = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;

Always test validation patterns with both valid and invalid inputs. A regex that passes everything is as useless as one that passes nothing.

Pitfalls and Best Practices

Regex is compact but easy to get wrong. These rules prevent most bugs:

  • Test incrementally. Write one piece of the pattern, test it, then add the next piece. Do not write a 40-character regex in one go.
  • Escape carefully. Inside a regex literal, a backslash is the escape character. To match a literal backslash, write two of them. To match a literal dot, escape it with a backslash.
  • Watch lastIndex with the global flag. If test() or exec() gives alternating results, the global flag is remembering state. Reset it by setting lastIndex back to zero, or remove the global flag entirely.
  • Regex is not a parser. Do not try to parse HTML, JSON, or nested structures with regex. Use an actual parser for those. Regex is for flat text patterns.
  • Prefer readable patterns. A pattern written with digit classes and repeat counts is clearer than one written as long strings of literal character ranges. Comment complex patterns if you can.

Many string methods accept regex patterns. See /javascript/javascript-string-methods-complete-guide for replace, split, and search with string arguments.

For capture groups, lookahead, lookbehind, and advanced replace patterns, see /javascript/regex-groups-flags-and-replace-in-javascript-guide.

Rune AI

Rune AI

Key Insights

  • A regex is a pattern for matching text, written as /pattern/flags in JavaScript.
  • test() returns true or false. exec() returns match details or null. match() and replace() work on strings.
  • Character classes like \d, \w, and [abc] match categories or specific sets of characters.
  • Quantifiers (*, +, ?, {n,m}) control how many times a pattern repeats.
  • Flags (g, i, m, s, u, y) change how the regex engine searches.
  • Build patterns incrementally and test each piece. Complex regex is hard to debug.
RunePowered by Rune AI

Frequently Asked Questions

Should I use regex literal or the RegExp constructor?

Use the literal /pattern/flags for static patterns. It is shorter and the pattern is compiled once. Use new RegExp(pattern, flags) only when the pattern string is built dynamically at runtime.

Why does my regex with the global flag give alternating results?

When you use the g flag with test() or exec(), the regex object remembers the last match position (lastIndex). Calling it again resumes from there. To reset, set regex.lastIndex = 0, or avoid the global flag with test() unless you are iterating through all matches.

Conclusion

Regular expressions are a dense but powerful language for pattern matching inside JavaScript. Start with the literal syntax, learn the core building blocks (character classes, quantifiers, anchors), and build patterns incrementally. Test every regex piece by piece instead of writing a complex pattern in one shot. Regex is a tool for specific text-matching problems, not a general-purpose parser. When your pattern grows beyond a few lines, consider whether a different approach would be more maintainable.