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.
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:
const pattern = /hello/;
console.log(pattern.test("hello world")); // true
console.log(pattern.test("goodbye")); // falseThe 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:
// 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
| Method | Belongs to | Returns | Use for |
|---|---|---|---|
| test(str) | RegExp | true or false | Checking if a pattern exists |
| exec(str) | RegExp | Match array or null | Getting match details, capture groups, and position |
| match(regex) | String | Match array or null | Getting all matches (with the g flag) or the first match |
| replace(regex, replacement) | String | New string | Replacing matched text |
| search(regex) | String | Index or -1 | Finding the position of a match |
| split(regex) | String | Array | Splitting 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:
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:
| Flag | Name | Effect |
|---|---|---|
| g | Global | Find all matches, not just the first |
| i | Case-insensitive | a matches A |
| m | Multiline | ^ and $ match start/end of each line, not just the whole string |
| s | Dot-all | the dot matches newline characters too |
| u | Unicode | Enable Unicode features like code point escapes and property escapes |
| y | Sticky | Match only from lastIndex, not anywhere in the string |
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-insensitiveThe 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:
| Class | Matches | Equivalent to |
|---|---|---|
| . | Any character except newline | (with the s flag: any character) |
| \d | Any digit | [0-9] |
| \D | Any non-digit | [^0-9] |
| \w | Word character (letter, digit, underscore) | [a-zA-Z0-9_] |
| \W | Non-word character | [^a-zA-Z0-9_] |
| \s | Whitespace (space, tab, newline) | [ \t\n\r] |
| \S | Non-whitespace | [^ \t\n\r] |
Custom character classes use square brackets:
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 digitInside 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:
| Quantifier | Meaning |
|---|---|
| * | 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 |
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 digitsBy 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:
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:
| Anchor | Matches |
|---|---|
| ^ | Start of string (or start of line with the m flag) |
| $ | End of string (or end of line with the m flag) |
| \b | Word boundary (between a word character and a non-word character) |
| \B | Non-word boundary |
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")); // falseAnchors 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:
// 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()orexec()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
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.
Frequently Asked Questions
Should I use regex literal or the RegExp constructor?
Why does my regex with the global flag give alternating results?
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.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.