Regex Tester
How This Regex Tester Works
Type a regular expression in the top box and the text to search below it. Every match is highlighted as you type, the status line counts the matches and capture groups, and the table lists each match with its position and the value of every group. Turn on Replace preview to see the text after a replace() with your replacement string, and copy the pattern as a JavaScript regex literal when it works.
This is a JavaScript regex tester, and only that. Your pattern is compiled with the browser's own new RegExp(pattern, flags) and run with String.prototype.matchAll, so what you see is exactly what your code will do in Chrome, Firefox, Safari, Node.js, Deno or Bun. It does not emulate PCRE, Python, Java or .NET. Most everyday syntax is shared, but the differences listed further down are real, and a pattern that passes here can fail elsewhere.
You can paste a whole literal such as /^\d+$/gm into the pattern box: the slashes are stripped and the flags are switched on for you. Patterns run in a background Web Worker. If one takes longer than 1.5 seconds — the symptom of catastrophic backtracking — the tester stops it instead of freezing your tab, and tells you why.
JavaScript Regex Flags Explained
Flags change how the whole pattern behaves. They go after the closing slash of a literal (/cat/gi) or in the second argument of the constructor (new RegExp('cat', 'gi')).
| Flag | Name | What it does |
|---|---|---|
| g | global | Find every match. Without it, match() and exec() stop at the first one, and replace() replaces only the first. |
| i | ignoreCase | /hello/i matches Hello and HELLO. |
| m | multiline | ^ and $ match at the start and end of every line, not only of the whole string. |
| s | dotAll | . also matches \n and \r. Without it, /a.b/ does not match "a\nb". |
| u | unicode | Treats the pattern as code points, so . matches a whole emoji instead of half of it, and enables \p{L}-style property classes. Also makes the syntax strict: unknown escapes become errors. |
| v | unicodeSets | Everything u does, plus set subtraction and intersection inside brackets, such as [\p{L}--[a-z]]. Cannot be combined with u. |
| y | sticky | Matches only at the exact position in lastIndex. /a/y does not match "ba" because there is no a at position 0. Used by tokenizers. |
| d | hasIndices | Adds start and end offsets for every group to the result (match.indices). The table above already shows each match's index, so the tester leaves it out. |
Regex Cheat Sheet
The tokens you will use most often, with the JavaScript behaviour. Try any of them in the regex tester above.
| Token | Matches | Example |
|---|---|---|
. | Any character except line breaks (any at all with s) | a.c → abc, a-c |
\d \w \s | Digit, word character [A-Za-z0-9_], whitespace | \d+ → 2026 |
\D \W \S | The opposite of each | \S+ → one word with punctuation |
[abc] [^abc] | One character from the set, or not from it | [aeiou] → each vowel |
^ $ | Start and end of the input (of each line with m) | ^\d{3}-\d{4}$ → 555-1234 |
\b | Word boundary | \bcat\b matches in “a cat sat”, not in “concatenate” |
* + ? | 0 or more, 1 or more, 0 or 1 | colou?r → color, colour |
{n} {n,m} | Exactly n, between n and m | \d{4} → 2026 |
*? +? | Lazy: as few as possible | <.+?> → <b> |
(…) | Capture group, numbered from 1 | (\d{4})-(\d{2}) |
(?<name>…) | Named capture group | (?<year>\d{4}) |
(?:…) | Group without capturing | (?:https?|ftp):// |
a|b | Either side | cat|dog |
(?=…) (?!…) | Lookahead: followed by / not followed by | \d+(?=px) → 12 in 12px |
(?<=…) (?<!…) | Lookbehind: preceded by / not preceded by | (?<=\$)\d+ → 42 in “cost $42” |
\1 \k<name> | Backreference to an earlier group | (\w)\1 → ll in hello |
Why Doesn't My Regex Match? 5 Classic Mistakes
When a pattern finds nothing, the tester retries it with each of the usual fixes and tells you which one makes it match on your text. It only shows a hint when the fix really works, so a hint is never a guess. These are the five mistakes behind most “regex not working” questions:
- Unescaped special characters.
. * + ? ^ $ ( ) [ ] { } | \are operators./example.com/also matchesexampleXcombecause the dot means “any character”; writeexample\.com. And/(1+1)/cannot find the text(1+1)at all — the tester suggests\(1\+1\). - Case sensitivity.
/hello/does not matchHello. Add theiflag, or write[Hh]elloif only the first letter varies. - Anchors without the multiline flag.
/^world/does not match the second line of"hello\nworld", because^means the start of the whole input. Withmit matches. The Multiline sample above shows this with a log file. - The dot does not cross lines.
/a.b/fails on"a\nb". Use thesflag or[\s\S]when a match should span line breaks. - Greedy quantifiers. On
<b>bold</b> and <i>italic</i>, the pattern/<.+>/greturns a single match — the entire string — because.+runs to the end and backs off only to the last>. The lazy/<.+?>/greturns the four tags. Load the Greedy trap sample to watch it happen.
Two more that the tester also catches: a pattern copied out of a JavaScript string with doubled backslashes (\\d is a literal backslash followed by d, not a digit), and a forgotten g flag, which leaves you with only the first match.
Capture Groups and Named Groups
Parentheses do two things: they group part of a pattern so a quantifier applies to all of it, and they capture the text that part matched. Groups are numbered by the position of their opening parenthesis. The pattern (\d{4})-(\d{2}) run on 2026-10-15 returns ["2026-10", "2026", "10"]: the whole match, then group 1, then group 2.
Named groups make that readable. With (?<year>\d{4})-(?<month>\d{2}), the same match carries match.groups = {"year":"2026","month":"10"}. Named groups still get numbers, so $1 keeps working. The table in the tester labels each column with its number and, when it has one, its name.
A group that did not take part in the match is undefined, not an empty string. /(x)?abc/ matches abc with group 1 undefined; the table shows the two cases differently. Use (?:…) when you need grouping but not the captured value — it keeps the numbering of the groups you care about stable.
Regex Find and Replace
The replacement string has its own small syntax. These are the patterns JavaScript recognises in the second argument of replace() and replaceAll():
| In the replacement | Inserts | Example → result |
|---|---|---|
$1, $2 … | The numbered group | /(\d{4})-(\d{2})-(\d{2})/ with $3/$2/$1: 2026-10-15 → 15/10/2026 |
$<name> | The named group | $<day>.$<month>.$<year> → 15.10.2026 |
$& | The whole match | /\d/g with [$&]: a1b2c3 → a[1]b[2]c[3] |
$` $' | The text before / after the match | Rarely needed |
$$ | A literal dollar sign | /x/ with $$: x → $ |
The replace preview uses your flags as they are. Without g, only the first match is replaced — the same thing that trips people up in code. An empty replacement deletes every match. The Named groups sample reformats ISO dates this way, which is also how you would rewrite the timestamps you check with the Unix timestamp converter.
JavaScript Regex vs PCRE and Python
Online regex testers often default to PCRE, the flavour used by PHP and many command-line tools. Patterns that work there can be syntax errors in JavaScript, or worse, valid but different. Checked against the JavaScript engine this page runs on:
| Feature | JavaScript | PCRE / Python |
|---|---|---|
| Named group | (?<name>…). (?P<name>…) is “Invalid group”. | Python uses (?P<name>…); PCRE accepts both. |
Possessive quantifier a++ | Syntax error: “Nothing to repeat”. | Supported in PCRE and Python 3.11+. |
Atomic group (?>…) | Syntax error: “Invalid group”. | Supported in PCRE and Python 3.11+. |
Inline flags (?i) at the start | Syntax error. Use the i flag instead. | Supported. |
\A and \Z | No error without u — but \A silently means the letter A. Use ^ and $. | Start and end of input. |
| Lookbehind | Supported, including variable length. | Fixed length only in PCRE and Python. |
\d and \w | ASCII only, even with u. /\d/ does not match the Arabic-Indic digit ٣. | Python 3 matches Unicode digits and letters by default. |
If you are writing a regex for another language, use this tester for the shared core and check the flavour-specific parts in that language. For JavaScript and TypeScript code, what you see here is the ground truth.
How to Use Regex in JavaScript Code
The four calls that cover almost everything, with the results they actually return:
const re = /(?<year>\d{4})-(?<month>\d{2})/;
re.test('2026-10-15'); // true
'2026-10-15'.match(re).groups; // { year: '2026', month: '10' }
[...'a1b22c333'.matchAll(/\d+/g)].map(m => m[0] + '@' + m.index);
// [ '1@1', '22@3', '333@6' ]
'2026-10-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'); // '15/10/2026'
'a,b;c d'.split(/[,; ]/); // [ 'a', 'b', 'c', 'd' ]
Two traps. First, a regex with the g or y flag remembers where it stopped in lastIndex, so calling .test() repeatedly on the same object gives different answers: with const r = /a/g, r.test('aa') returns true, true, then false. Do not reuse a global regex for yes/no checks. Second, in new RegExp('\\d+') every backslash must be doubled because the pattern is a string first; the literal /\d+/ needs no doubling. The JavaScript box in the tester gives you both forms, already escaped.
In Python the same date rewrite is re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', '2026-10-15'), which also returns 15/10/2026 — note \3 instead of $3 and the raw string prefix. Regexes are also a common way to pull fields out of text before converting it to structured data; for whole documents, a parser such as the JSON formatter or the XML to JSON converter is more reliable than a pattern.
Catastrophic Backtracking
JavaScript's regex engine backtracks: when part of a pattern fails, it goes back and tries every other way the earlier parts could have matched. For most patterns that is instant. For a pattern with a quantifier inside a quantifier, like /^(a+)+$/, a string of a few dozen as followed by a b has an exponential number of ways to split, and the engine tries them all before giving up. A page that runs such a pattern on user input freezes; a server can be taken down by one request (a “ReDoS”).
This tester runs each pattern in a separate worker with a 1.5-second limit, so you can safely test it and see the failure. The fix is to remove the ambiguity: /^a+$/ matches the same strings as /^(a+)+$/, and alternatives inside a repeated group should not be able to match the same text.
Frequently Asked Questions
/\d+/g and the flags are set for you.RegExp, so results match Chrome, Firefox, Safari and Node.js exactly. It does not emulate PCRE, Python, Java or .NET; patterns using possessive quantifiers, atomic groups or (?P<name>) are syntax errors here.g (global) flag is off. Without it, match(), exec() and replace() stop after the first match. The tester points this out whenever turning g on would find more.m (multiline) flag, ^ and $ only match at the start and end of the entire string. Turn on m to make them match at the start and end of every line.$1, $2 and so on for numbered groups, $<name> for named groups, and $& for the whole match. For example, /(\d{4})-(\d{2})-(\d{2})/ with $3/$2/$1 turns 2026-10-15 into 15/10/2026. Use $$ for a literal dollar sign.*, +, {n,}) match as much as they can; lazy ones (*?, +?) match as little as they can. On <b>bold</b>, /<.+>/ matches the whole string while /<.+?>/ matches just <b>.(a+)+ can take exponential time on input that almost matches. This tester runs patterns in a background worker and stops them after 1.5 seconds, so the page stays responsive and you see a warning instead.