Regex Tester

Runs in your browser with the JavaScript regex engine. Your pattern and text are never uploaded.
Flags
Matches
JavaScript

    

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')).

FlagNameWhat it does
gglobalFind every match. Without it, match() and exec() stop at the first one, and replace() replaces only the first.
iignoreCase/hello/i matches Hello and HELLO.
mmultiline^ and $ match at the start and end of every line, not only of the whole string.
sdotAll. also matches \n and \r. Without it, /a.b/ does not match "a\nb".
uunicodeTreats 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.
vunicodeSetsEverything u does, plus set subtraction and intersection inside brackets, such as [\p{L}--[a-z]]. Cannot be combined with u.
ystickyMatches only at the exact position in lastIndex. /a/y does not match "ba" because there is no a at position 0. Used by tokenizers.
dhasIndicesAdds 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.

TokenMatchesExample
.Any character except line breaks (any at all with s)a.c → abc, a-c
\d \w \sDigit, word character [A-Za-z0-9_], whitespace\d+ → 2026
\D \W \SThe 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
\bWord boundary\bcat\b matches in “a cat sat”, not in “concatenate”
* + ?0 or more, 1 or more, 0 or 1colou?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|bEither sidecat|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:

  1. Unescaped special characters. . * + ? ^ $ ( ) [ ] { } | \ are operators. /example.com/ also matches exampleXcom because the dot means “any character”; write example\.com. And /(1+1)/ cannot find the text (1+1) at all — the tester suggests \(1\+1\).
  2. Case sensitivity. /hello/ does not match Hello. Add the i flag, or write [Hh]ello if only the first letter varies.
  3. Anchors without the multiline flag. /^world/ does not match the second line of "hello\nworld", because ^ means the start of the whole input. With m it matches. The Multiline sample above shows this with a log file.
  4. The dot does not cross lines. /a.b/ fails on "a\nb". Use the s flag or [\s\S] when a match should span line breaks.
  5. Greedy quantifiers. On <b>bold</b> and <i>italic</i>, the pattern /<.+>/g returns a single match — the entire string — because .+ runs to the end and backs off only to the last >. The lazy /<.+?>/g returns 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 replacementInsertsExample → 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 matchRarely 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:

FeatureJavaScriptPCRE / 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 startSyntax error. Use the i flag instead.Supported.
\A and \ZNo error without u — but \A silently means the letter A. Use ^ and $.Start and end of input.
LookbehindSupported, including variable length.Fixed length only in PCRE and Python.
\d and \wASCII 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

How do I test a regular expression online?
Type the pattern into the regex tester above, switch on the flags you need, and paste your text below it. Matches are highlighted as you type, and the table shows each match's position and capture groups. You can also paste a complete literal like /\d+/g and the flags are set for you.
Which regex flavour does this tester use?
JavaScript only. The pattern is compiled with your browser's built-in 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.
Why does my regex only find the first match?
The 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.
Why doesn't ^ or $ match in the middle of my text?
Without the 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.
How do I use a capture group in the replacement?
Write $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.
What is the difference between greedy and lazy quantifiers?
Greedy quantifiers (*, +, {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>.
Can a regex freeze my browser?
Yes. Nested quantifiers such as (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.
Is my text sent anywhere?
No. Matching happens in your browser; nothing is uploaded or stored. You can disconnect from the internet after the page loads and the regex tester keeps working.