Regular expressions can look like someone dropped a box of punctuation onto the keyboard and decided it was code. They are confusing at first, but underneath all those slashes, brackets, and question marks is a small language with a manageable set of rules.
This guide uses JavaScript regex syntax. Many ideas also apply to other languages, but each regex engine has a few rules of its own.
What’s a Regex, Anyway?
In the simplest terms, a regular expression, usually shortened to regex, is a sequence of characters that defines a search pattern. You can think of it as a mini-language for searching, replacing, validating, and extracting text in a flexible way.
For example:
// Find every occurrence of "hello"
const regex = /hello/g;
console.log("hello there, hello again".match(regex));
// ["hello", "hello"]
A normal string search asks for one exact value. A regex can ask for broader patterns, such as “one or more digits,” “a word at the start of a line,” or “either color or colour.” That is where its strange-looking syntax starts earning its keep.
Anatomy of a Basic Regex
In JavaScript, a regex literal usually consists of three main parts:
- Delimiters: Forward slashes
/that enclose the pattern. - Pattern: The sequence of characters defining what you want to match.
- Flags: Optional letters that modify how matching behaves.
Example:
/hello/gi
- The first and last
/are the delimiters. hellois the pattern and matches that exact text.gfinds every match instead of stopping after the first.imakes the search case-insensitive.
A semicolon after /hello/gi; belongs to the JavaScript statement, not to the regex.
When a pattern must be created dynamically, JavaScript also provides the RegExp constructor:
const regex = new RegExp("\\d+", "g");
Because the pattern is inside a JavaScript string, its backslash must be escaped. The equivalent regex literal is simply /\d+/g. Yes, regex sometimes makes you escape the escape. Nobody said the mini-language would be emotionally supportive.
Meta-Characters in Regex
Most characters match themselves, but metacharacters have special meaning.
1. . (Dot)
The dot matches any single character except line terminators by default.
/c.t/.test("cat"); // true
/c.t/.test("cot"); // true
/c.t/.test("cut"); // true
/c.t/.test("coat"); // false
With the s flag, the dot can match line terminators too.
2. ^ (Caret)
The caret asserts the start of a string, or the start of a line when multiline mode is enabled.
/^cat/.test("cat naps"); // true
/^cat/.test("a cat naps"); // false
3. $ (Dollar Sign)
The dollar sign asserts the end of a string, or the end of a line in multiline mode.
/cat$/.test("copycat"); // true
/cat$/.test("cat naps"); // false
Use both anchors when the whole input must match:
/^cat$/.test("cat"); // true
/^cat$/.test("cat naps"); // false
4. * (Asterisk)
The asterisk matches the preceding element zero or more times.
/ca*t/.test("ct"); // true
/ca*t/.test("cat"); // true
/ca*t/.test("caaat"); // true
5. + (Plus)
The plus sign matches the preceding element one or more times.
/ca+t/.test("cat"); // true
/ca+t/.test("caaat"); // true
/ca+t/.test("ct"); // false
6. ? (Question Mark)
The question mark makes the preceding element optional by matching it zero or one time.
/colou?r/.test("color"); // true
/colou?r/.test("colour"); // true
It can also make another quantifier lazy, meaning it consumes as little text as possible:
const text = 'He said "hello" and then "goodbye".';
console.log(text.match(/".*"/g));
// ['"hello" and then "goodbye"']
console.log(text.match(/".*?"/g));
// ['"hello"', '"goodbye"']
7. {} (Curly Braces)
Curly braces specify how many times the preceding element should appear.
/a{3}/.test("aaa"); // exactly three
/a{3,}/.test("aaaaa"); // at least three
/a{3,6}/.test("aaaa"); // between three and six
JavaScript supports {n}, {n,}, and {n,m}. It does not support {,m}, so use something like {0,6} when the lower bound is zero.
8. [] (Square Brackets)
Square brackets define a character class. The class matches one character from the set.
/[aeiou]/.test("cloud"); // true
/[aeiou]/.test("sky"); // false
9. | (Pipe)
The pipe acts as an OR operator.
/cat|dog/.test("I have a dog"); // true
Alternation has low precedence, so grouping is important when other rules must apply to every option:
/^cat|dog$/.test("cat naps"); // true, probably not intended
/^(?:cat|dog)$/.test("cat naps"); // false
10. () (Parentheses)
Parentheses group patterns and, by default, capture the matched content.
/(cat|dog)/.exec("I have a dog");
// captures "dog"
11. \ (Backslash)
The backslash either escapes a special character or starts a special sequence.
/\./.test("version 1.0"); // matches a literal dot
/\$10/.test("Price: $10"); // matches a literal dollar sign
Some Regex Shorthand Character Classes
Regex provides shortcuts for common character sets.
1. \d
- Meaning: Matches an ASCII digit from
0to9. - Equivalent:
[0-9].
/\d+/.exec("Room 204"); // matches "204"
2. \D
- Meaning: Matches any character that is not an ASCII digit.
- Equivalent:
[^0-9].
/\D+/.exec("abc123"); // matches "abc"
3. \w
- Meaning: Matches an ASCII letter, digit, or underscore.
- Equivalent:
[A-Za-z0-9_]in normal usage.
/\w+/.exec("hello_world123"); // matches "hello_world123"
4. \W
- Meaning: Matches any character outside
\w.
/\W+/.exec("hello, world"); // matches ", "
5. \s
- Meaning: Matches whitespace and line-terminator characters, including spaces, tabs, newlines, and Unicode whitespace.
/\s+/.test("hello world"); // true
6. \S
- Meaning: Matches any non-whitespace character.
/\S+/.exec(" hello world"); // matches "hello"
7. \b
- Meaning: Matches a word boundary, which is a position between a word and non-word character.
/\bcat\b/.test("a cat sleeps"); // true
/\bcat\b/.test("caterpillar"); // false
8. \B
- Meaning: Matches a position that is not a word boundary.
/\Bcat/.test("bobcat"); // true
/\Bcat/.test("cat"); // false
JavaScript’s \w, \b, and \B are mostly ASCII-oriented. For international text, Unicode property escapes are often better:
/^\p{L}+$/u.test("東京"); // true: Unicode letters
/^[\p{L}\p{M}]+$/u.test("नमस्ते"); // true: letters and combining marks
What About \A and \Z?
Some regex engines use \A for the absolute beginning of a string and \Z for its end. JavaScript does not support either one. Use ^ and $, and add the m flag only when those anchors should work per line.
Capture Groups in Regex
Capture groups let you extract and reuse specific parts of matched text. They are defined with parentheses () and are useful for grouping, parsing, and replacement.
How to Use Capture Groups
Creating a Capture Group
Place the pattern you want to capture inside parentheses:
const match = /(cat)/.exec("a cat sleeps");
console.log(match?.[1]); // "cat"
The complete match is stored at index 0, and the first captured group is stored at index 1.
Using Multiple Capture Groups
Groups are numbered from left to right by the position of their opening parenthesis.
const match = /(cat) and (dog)/.exec("cat and dog");
console.log(match?.[1]); // "cat"
console.log(match?.[2]); // "dog"
Non-Capturing Groups
Sometimes you need to group alternatives or apply a quantifier but do not need the result in the captured output. Use (?:...):
/(?:cat|dog) house/.test("dog house"); // true
The group still organizes the pattern, but it does not add another numbered capture.
Converting a Capture Group to a Non-Capturing Group
Add ?: immediately after the opening parenthesis.
// Capturing group
/(cat|dog)/
// Non-capturing group
/(?:cat|dog)/
Use capturing groups when you need the matched part later. Otherwise, non-capturing groups keep the result and numbering easier to understand.
Naming Capture Groups
Named groups use (?<name>...). They make extracted data much clearer than remembering whether the year was group two or group three.
const pattern = /^(?<day>\d{2})-(?<month>\d{2})-(?<year>\d{4})$/;
const match = pattern.exec("23-09-2024");
if (match?.groups) {
console.log(match.groups.day); // "23"
console.log(match.groups.month); // "09"
console.log(match.groups.year); // "2024"
}
Named groups are also useful during replacement:
const result = "23-09-2024".replace(
pattern,
"$<year>-$<month>-$<day>"
);
console.log(result); // "2024-09-23"
Captured groups can even be referenced inside the regex. This pattern finds a repeated word:
const repeatedWord = /\b(\w+)\s+\1\b/gi;
console.log("This is is repeated".match(repeatedWord));
// ["is is"]
Character Classes and Negation in Regex
Character classes specify a set of possible characters. A class matches one character from that set, while a negated class matches one character outside it.
Character Classes
1. Basic Character Class
/[abc]/
- Meaning: Matches
a,b, orc. - Example: It finds
cincatandbinbat.
2. Character Ranges
/[a-z]/
- Meaning: Matches one lowercase ASCII letter from
atoz.
3. Multiple Ranges
/[A-Za-z0-9]/
- Meaning: Matches one uppercase letter, lowercase letter, or digit.
4. Combining Characters and Ranges
/[aeiou0-9]/
- Meaning: Matches one vowel or digit.
Inside a character class, most metacharacters lose their special meaning. For example, . means a literal dot. A hyphen creates a range when placed between characters; put it first or last, or escape it, when you need a literal hyphen.
/[.-]/.test("file-name"); // true
Negated Character Classes
Place ^ immediately after [ to match characters that are not in the set.
/[^a-z]/
- Meaning: Matches one character that is not a lowercase ASCII letter.
- Example: It can match
1,A, or#.
The caret negates only when it appears at the beginning of the class. In [a^b], it is just a literal caret.
Common Use Cases for Negation
1. Matching Non-Digit Characters
/[^0-9]+/.exec("abc123"); // matches "abc"
2. Matching Non-Whitespace Characters
/[^\s]+/.exec("hello world"); // matches "hello"
This is equivalent to using \S+.
3. Matching Non-Alphanumeric Characters
/[^A-Za-z0-9]+/.exec("hello@#!"); // matches "@#!"
Using Character Classes with Quantifiers
Character classes become more useful when combined with quantifiers.
/[^0-9]+/.exec("abc123XYZ"); // matches "abc"
/[^A-Za-z]+/.exec("hello123!"); // matches "123!"
/[A-F0-9]{6}/.test("7C3AED"); // true
The class chooses what one character may be; the quantifier decides how many of those characters may appear.
Commonly Used Regex Flags
Flags modify regex behavior. JavaScript supports several flags, but the following are the ones beginners will use most often.
1. i (Ignore Case)
Makes matching case-insensitive.
"Hello HELLO hello".match(/hello/gi);
// ["Hello", "HELLO", "hello"]
2. g (Global)
Finds all matches instead of only the first.
"cat and catalog and catnip".match(/cat/g);
// ["cat", "cat", "cat"]
3. m (Multiline)
Makes ^ and $ work at the start and end of each line. Use it with g when you want every matching line.
const input = "test line 1\nanother line\ntest line 2";
console.log(input.match(/^test/gm));
// ["test", "test"]
4. s (Dot All)
Allows . to match line terminators.
/hello.world/s.test("hello\nworld"); // true
5. u (Unicode)
Enables Unicode-aware parsing and code-point behavior, including Unicode property escapes.
/^\p{L}+$/u.test("こんにちは"); // true
6. x (Extended)
The original draft included the x flag because some regex engines support readable whitespace and comments inside patterns. JavaScript does not support it.
If a JavaScript regex becomes complicated, split it into documented pieces or explain each part beside the code instead of pasting an x-flag pattern that will throw a syntax error.
JavaScript also has advanced flags such as d, v, and y, but g, i, m, s, and u cover most everyday work.
Putting Regex to Work in JavaScript
JavaScript provides a few common ways to use a regex.
Test Whether a Match Exists
const hasError = /error/i.test("Database ERROR");
console.log(hasError); // true
Find Matches
const words = "one, two, three".match(/\w+/g);
console.log(words); // ["one", "two", "three"]
Use matchAll() when you need all matches and their capture groups:
const input = "name=Tilak role=developer";
const pairs = [...input.matchAll(/(?<key>\w+)=(?<value>\w+)/g)];
for (const pair of pairs) {
console.log(pair.groups);
}
Search and Replace
const cleaned = "too many spaces".replace(/\s+/g, " ");
console.log(cleaned); // "too many spaces"
Split Text
const values = "red, green;blue | yellow".split(/\s*[,;|]\s*/);
console.log(values); // ["red", "green", "blue", "yellow"]
Common Use Cases of Regex
- Data validation: Checking whether input has an expected shape, such as a date, username, or color code.
- Search and replace: Finding repeated spaces, formatting text, or replacing captured values.
- Text parsing: Extracting controlled values such as hashtags, IDs, timestamps, or log levels.
- Log analysis: Finding error messages, request IDs, IP-like values, or timestamps in predictable log formats.
- Web scraping: Matching narrow, controlled text fragments. For arbitrary HTML, use
DOMParseror a proper HTML parser; HTML has enough edge cases to make a heroic regex regret its career choices.
Commonly Used Regex Patterns
These patterns are useful starting points, not universal validators.
1. Simple Email Shape
const simpleEmailShape = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
This catches common formatting mistakes, but it is not a complete implementation of every valid email address. Real email verification still requires sending a verification message.
2. One Specific Phone Number Shape
const phoneShape = /^(?:\+[1-9]\d{0,2}[-. ]?)?\d{3}[-. ]?\d{3}[-. ]?\d{4}$/;
This demonstrates optional country codes and separators for one narrow structure. International phone numbers vary widely, so use a library such as libphonenumber for production validation.
3. URL Validation
URLs have more rules than a short regex usually handles. JavaScript already has a parser:
function isHttpUrl(value) {
try {
const url = new URL(value);
return url.protocol === "http:" || url.protocol === "https:";
} catch {
return false;
}
}
Sometimes the best regex is no regex. Character growth achieved.
4. Date Format (YYYY-MM-DD)
const isoDateShape = /^\d{4}-\d{2}-\d{2}$/;
This checks the shape only. It still accepts impossible dates such as 2026-99-99, so parse the date when calendar validity matters.
5. Hexadecimal Color Code
const hexColor = /^#?(?:[A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/;
It accepts three- or six-digit hexadecimal colors, with an optional leading #.
6. Extract Unicode Hashtags
const post = "Learning #JavaScript with #नमस्ते and #東京";
const hashtags = post.match(/#[\p{L}\p{M}\p{N}_]+/gu);
console.log(hashtags);
// ["#JavaScript", "#नमस्ते", "#東京"]
7. Parse a Controlled Log Entry
const logPattern =
/^\[(?<time>\d{2}:\d{2}:\d{2})\] (?<level>INFO|WARN|ERROR): (?<message>.+)$/;
const entry = logPattern.exec("[14:32:08] ERROR: Connection timed out");
console.log(entry?.groups);
// { time: "14:32:08", level: "ERROR", message: "Connection timed out" }
A Few Regex Traps Worth Knowing
Global Patterns Remember Their Position
Patterns using g or y update their lastIndex when used with test() or exec().
const pattern = /cat/g;
pattern.test("cat"); // true
pattern.test("cat"); // false: matching resumed after the first result
pattern.lastIndex = 0;
Remove g when you need only a yes-or-no test.
Escape Dynamic User Input
User text may contain regex syntax such as ., *, or (. Modern JavaScript provides RegExp.escape() for turning it into a literal pattern safely.
const searchText = "file.js";
const pattern = new RegExp(RegExp.escape(searchText), "gi");
pattern.test("Open FILE.JS"); // true
Avoid Dangerous Backtracking
Complex nested quantifiers can take a very long time on carefully chosen input, creating a regular expression denial-of-service problem. Keep patterns understandable, test long near-matches, and never execute arbitrary user-provided regex without safeguards.
Conclusion
Regular expressions are the Swiss Army knife of text processing: versatile, powerful, and slightly confusing when you first unfold all the tools. They can validate a format, search logs, extract data, replace text, and turn repetitive string work into a small pattern.
Start with literal text. Add character classes, quantifiers, anchors, and groups one piece at a time. Test values that should match, values that should fail, and the awkward edge cases hiding between them. Also remember that regex checks patterns, not truth: 2026-99-99 can look like a date while being absolutely useless to a calendar.
Embrace the quirks, keep a regex tester nearby, and soon the mysterious wall of punctuation will start looking like a useful tool instead of an encrypted cry for help.
For a complete syntax reference, see MDN’s JavaScript regular expressions guide and character class reference.