A regex tester that runs JavaScript's own engine
A regular expression (regex) is a small pattern that describes text: "four digits, a dash, two digits", "a word followed by @", "any line that starts with a number". This regex tester runs your pattern with the same engine your browser uses for JavaScript, so what matches here matches in Node.js, Deno, Bun and front-end code too.
Every match is highlighted in the test string, in two alternating colors so neighbors stay apart. A match of zero length, like the position that ^ or \b finds, shows as a thin line. The Matches tab lists each match with its start and end position and the text of every group, numbered and named. Positions count UTF-16 code units, just like JavaScript's string indexes, so most emoji count as 2.
The search runs in a background worker with a time limit of one second. If a pattern explodes on your text, the tester stops it and tells you, instead of freezing the tab.
How to use the regex tester
- 1Type a pattern, or pick a common one like Email or US phone. You can also paste a literal such as /colou?r/gi and the flags are set for you.
- 2Turn flags on or off and edit the test string. Matches light up as you type and the count shows above the text.
- 3Check groups in Matches, try a replacement in Replace, read the explanation in Explain, then copy the regex, the code or a link.
Regex cheat sheet
The building blocks you will use most. Put a backslash in front of a special character to match it literally.
| Syntax | Means | Example |
|---|---|---|
| . | Any character except a line break | c.t finds cat, cot, c9t |
| \d \w \s | A digit, a word character (letter, digit, _), a whitespace character | \d\d finds 42 |
| \D \W \S | The opposite: not a digit, not a word character, not whitespace | \S+ finds each word |
| [abc] [a-z] | One character from the list or range | gr[ae]y finds gray and grey |
| [^abc] | One character that is not in the list | [^0-9] finds a non-digit |
| ^ $ | Start and end of the text (of each line with the m flag) | ^Hi finds Hi at the start |
| \b | Word boundary | \bcat\b skips category |
| * + ? | Zero or more, one or more, optional | colou?r finds color and colour |
| {3} {2,} {2,5} | Exactly 3, 2 or more, 2 to 5 times | \d{5} finds a ZIP code |
| *? +? | Lazy: as few as possible | <.+?> stops at the first > |
| (abc) | Capturing group, reused as $1 or \1 | (\w+)@ captures the name |
| (?<name>abc) | Named group, reused as $<name> or \k<name> | (?<year>\d{4}) |
| (?:abc) | Group without capturing | (?:ab)+ finds abab |
| a|b | Either side | cat|dog |
| (?=x) (?!x) | Followed by x, not followed by x | \d+(?=px) finds 12 in 12px |
| (?<=x) (?<!x) | Preceded by x, not preceded by x | (?<=\$)\d+ finds 5 in $5 |
| \p{L} | Any letter in any language (needs u or v) | \p{L}+ finds café and Ελλάδα |
| \. \( \\ | A special character taken literally | \.com finds .com |
Regex flags explained
Flags change how the whole pattern behaves. In code they go after the closing slash, like /cat/gi, or into the second argument of new RegExp("cat", "gi").
| Flag | Name | What it does |
|---|---|---|
| g | global | Find all matches, not only the first one. |
| i | ignore case | Upper and lower case letters match each other. |
| m | multiline | ^ and $ match at the start and end of every line, not only of the whole text. |
| s | dotAll | The dot also matches line breaks. |
| u | unicode | Treats emoji and other characters above U+FFFF as one character, enables \p{...} and makes the syntax strict. |
| v | unicodeSets | Everything u does, plus nested classes, set subtraction and intersection, and string properties. |
| y | sticky | A match must start exactly at lastIndex. There is no searching ahead. |
| d | indices | Reports where each group starts and ends (match.indices). |
The global flag matters most in practice: without it, match() returns only the first match and replace() changes only the first one. The multiline flag only changes ^ and $; to let the dot cross line breaks you need the s flag. Turn on u (or the newer v) whenever your text may contain emoji or letters outside the basic range, otherwise a dot matches only half of an emoji.
JavaScript regex compared with other flavors
Most syntax is shared by JavaScript, Python, PHP (PCRE), Java and .NET. These differences trip people up most often:
- Named groups are written (?<name>...) and reused with \k<name> or $<name>. Python's (?P<name>...) is a syntax error in JavaScript.
- No \A, \Z or \z. Use ^ and $ without the m flag. Also, $ in JavaScript matches only at the very end, not before a final line break as in PCRE and Python.
- No possessive quantifiers or atomic groups. a++ and (?>...) are errors. A lookahead with a backreference, (?=(a+))\1, can imitate an atomic group.
- \d and \w are ASCII only, even with the u flag. Use \p{Nd} or \p{L} with u for digits and letters in every script.
- Lookbehind can have any length in JavaScript, which many other engines do not allow.
- Inline flags such as (?i) at the start are not supported. Recent browsers accept modifier groups like (?i:abc) for part of a pattern.
- Replacements use $1 and $<name>, not \1 as in sed or Python's re.sub.
- In a regex literal a forward slash must be escaped as \/. With new RegExp() you pass a string, so every backslash has to be doubled: "\\d+". The Code tab handles this for you.
Performance tips and catastrophic backtracking
JavaScript's regex engine backtracks: when part of a pattern fails, it goes back and tries the other ways the earlier parts could have matched. Usually that is fast. But a pattern like (a+)+$ on the text "aaaaaaaaaaaaaaaaaaaaaaaaaaaaab" has an exponential number of ways to split the a's between the inner and outer repeat, and it tries all of them before it admits there is no match. Each extra "a" roughly doubles the time. On a server, one such input can block a process for minutes, which is why it is known as a ReDoS risk.
- Never nest repeats that can match the same text: (a+)+, (\w*)*, (\s*,\s*)+. Usually the outer repeat is enough: a+.
- Make alternatives exclusive. (\d|\d\d)+ gives the engine many ways to match the same digits; \d+ gives one.
- Prefer a specific class to .* For quoted text, "[^"]*" is faster and safer than ".*?".
- Anchor when you can. ^ and $ or \b stop the engine from trying every starting position.
- Create the regex once, outside loops, and use plain string methods like includes() or startsWith() when you do not need a pattern.
- Limit input length before matching untrusted text on a server.
Try it: load any pattern, type (a+)+$ as the regex and a long row of a's followed by b as the test string. The tester stops after one second and explains what happened.
Frequently asked questions
How do I test a regular expression in JavaScript?
Type the pattern in the regex field, choose flags and paste some text. Every match is highlighted as you type, and the Matches tab lists each match with its position and groups. In your own code, re.test(text) answers yes or no, text.match(re) and text.matchAll(re) return the matches, and text.replace(re, ...) replaces them. The Code tab writes these lines for your pattern, ready to copy.
Why does my regex only find the first match?
Without the g (global) flag a JavaScript regex stops after the first match. Turn on g to find all of them. The same applies to replace: without g only the first match is replaced, and matchAll() and replaceAll() refuse a regex without g.
Why does test() return true and then false?
A regex with the g or y flag remembers where the last match ended in its lastIndex property. The next test() call starts searching from there, so a second call on the same text can fail. For a plain yes or no check, leave g off, or set re.lastIndex = 0 before each call.
What does "This pattern took too long" mean?
Some patterns, especially nested repeats like (a+)+ or (\w*)*, make the regex engine try an exponential number of ways to match before it gives up. This is called catastrophic backtracking. The tester runs your pattern in a background worker and stops it after one second, so the page stays responsive. Rewrite the nested repeat, for example (a+)+ as a+.
Is there a regex that checks every email address correctly?
No. The official email rules allow quoted names, comments and other rare forms that no readable regex covers, and a valid format does not prove the mailbox exists. Use a simple pattern to catch typos and send a confirmation email to be sure.
Can I test Python, PHP or Java regex here?
This tester uses your browser's JavaScript regex engine. Basic syntax like classes, quantifiers, groups and lookarounds works the same in most flavors, but details differ: Python writes named groups as (?P<name>...), PHP and Java support possessive quantifiers and atomic groups, and JavaScript has no \A or \Z. The explanation warns you about common syntax from other flavors.
Is my text uploaded?
No. Matching, replacing and the explanation all run in your browser. Copy link puts the pattern, flags, replacement and the first 500 characters of the test string into the link itself, so do not share a link that contains private text.