Loading tool...
Loading tool...
Regex & Pattern Matching Guides
Regular expressions are short patterns for finding, checking and changing text. They are hard to read and easy to get slightly wrong, so the quickest way to write one is to try it on real text and watch what it matches.
This guide covers the JavaScript regex syntax you will use most, five worked examples, how to use a tested pattern in code, and the mistakes that cause most surprises. Every example here was run in the DevMeldex Regex Tester, which uses your browser's own JavaScript regex engine.
Try the examples as you read:
Open the Regex Tester →Type the pattern without the surrounding slashes, paste some sample text, and switch on the flags you need: g to find every match, i to ignore case, m so ^ and $ work per line, and s so a dot also matches line breaks. Matches are highlighted as you type, and the match details show where each match starts and what each group captured.
The tester runs JavaScript regex only. The Regex Tester page lists which features from other languages are not available, such as Python's (?P<name>...) groups and PCRE's atomic groups.
These are the building blocks you will use in almost every pattern.
| Token | Meaning |
|---|---|
. | Any character except a line break (with the s flag, line breaks too) |
\d | A digit, 0 to 9 |
\w | A letter, digit or underscore |
\s | Whitespace: space, tab or line break |
\D \W \S | The opposite of \d, \w and \s |
[abc] | One character from the list |
[^abc] | One character not in the list |
[a-z0-9] | One character from a range |
\. | A literal dot; escape any special character with \ |
| Token | Meaning |
|---|---|
^ | Start of the text (with the m flag, start of each line) |
$ | End of the text (with the m flag, end of each line) |
\b | A word boundary, such as the edge of cat in "a cat." |
\B | A position that is not a word boundary |
| Token | Meaning |
|---|---|
* | Zero or more of the previous item |
+ | One or more of the previous item |
? | Zero or one: the previous item is optional |
{3} | Exactly 3 times |
{2,4} | Between 2 and 4 times |
{2,} | 2 or more times |
*? +? ?? | Lazy versions: match as few characters as possible |
| Token | Meaning |
|---|---|
(abc) | Capturing group: the match is kept as group 1, 2 and so on |
(?<year>\d{4}) | Named capturing group, read as groups.year in code |
(?:abc) | Non-capturing group: groups without keeping the match |
cat|dog | Either side of the | |
\1 | The same text that group 1 matched |
\k<year> | The same text that the group named year matched |
| Token | Meaning |
|---|---|
(?=abc) | Followed by abc, without including it in the match |
(?!abc) | Not followed by abc |
(?<=abc) | Preceded by abc, without including it in the match |
(?<!abc) | Not preceded by abc |
Paste each pattern and text into the tester and set the flags shown. The results below are what the tester returns.
Pattern: (?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
Flags: g
Text: Released 2026-04-22, updated 2026-05-16Two matches, 2026-04-22 and 2026-05-16. The match details list each match's groups by position: 2026, 04, 22 for the first and 2026, 05, 16 for the second.
The pattern only checks the shape of a date, so it also accepts impossible dates such as 2026-13-45.
Pattern: \bcat\b Flags: gi Text: Cat, catalog, cat.
Two matches: Cat and cat. The word boundaries stop it matching inside catalog. Without the i flag, only the lowercase cat matches.
Pattern: <.+> compared with <.+?> Flags: g Text: <b>bold</b>
The greedy <.+> takes as much as it can and returns one match, the whole <b>bold</b>. The lazy <.+?> stops as early as possible and returns two matches, <b> and </b>.
Pattern: (?<=\$)\d+(?:\.\d{2})?
Flags: g
Text: Total: $42.50, tax $3Two matches, 42.50 and 3. The lookbehind requires a $ before the number but leaves it out of the match, and the non-capturing group makes the cents optional.
Pattern: ^ERROR.*$ Flags: gm Text: INFO start\nERROR disk full\nINFO retry\nERROR timeout
Two matches: ERROR disk full and ERROR timeout. Without the m flag, ^ and $ only match at the start and end of the whole text, so there is no match at all.
Once a pattern works in the tester, it behaves the same way in JavaScript with the same flags. These are the methods you will use most often:
// test(): does the text match at all?
/^\d{5}$/.test("12345"); // true
/^\d{5}$/.test("1234"); // false
// match() with the g flag: every match as an array
"a1 b22 c333".match(/\d+/g); // ["1", "22", "333"]
// matchAll(): every match with its groups
const text = "Released 2026-04-22, updated 2026-05-16";
for (const m of text.matchAll(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g)) {
console.log(m.groups.year); // "2026", then "2026"
}
// replace(): reuse groups in the replacement
"2026-04-22".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"); // "22/04/2026"
"2026-04-22".replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, "$<d>/$<m>/$<y>"); // "22/04/2026"In a replacement string, $1 inserts group 1 and $<name> inserts a named group.
\d+ on a1 b22 c333 finds only 1. With it, you get 1, 22 and 333..* and .+ take as much text as they can. Use the lazy .*? or a negated class such as [^<]* instead.example.com also matches exampleXcom, because a dot means any character. Write example\.com.\d{5} finds 12345 inside 123456. To check that the whole value is five digits, use ^\d{5}$."\d" is just the letter d, so the backslash never reaches the regex:new RegExp("\d+").test("123"); // false: "\d" in a string is just "d"
new RegExp("\\d+").test("123"); // true
/\d+/.test("123"); // true: no extra escaping in a regex literalconst re = /a/g;
re.test("a"); // true
re.test("a"); // false: the search continued from the end of the last match
re.test("a"); // trueDrop the g flag when you only need a yes or no answer. Also note that replaceAll() throws a TypeError if you pass it a regex without the g flag. Finally, patterns with nested quantifiers such as (a+)+ can take exponentially longer as the text grows; the Regex Tester page explains this catastrophic backtracking and how the tester stops slow patterns.
Most of the syntax above works in Python and Java too, but some details differ, so test the final pattern in the language where it will run:
(?P<year>...). JavaScript reports that as an invalid group, and Python 3.12 rejects JavaScript's (?<year>...) form.re.IGNORECASE, re.MULTILINE and re.DOTALL in Python, and to CASE_INSENSITIVE, MULTILINE and DOTALL in Java's Pattern class. There is no g flag: Python uses re.findall() or re.finditer() to get every match.\d+ becomes "\\d+" in Java code. Java writes named groups the same way as JavaScript.Write the smallest pattern that works, test it against real examples including the edge cases you expect, then anchor it if you are validating whole values. Turn on only the flags you need, and test the pattern again in the language where it will run.
Discover browser-based tools for formatting, debugging, validation and testing.
Browse All Tools →