Free · Live · Nothing uploaded

Regex tester

Build and debug regular expressions with live match highlighting, capture groups, flags and a replace preview. JavaScript flavor, right in your browser.

/ /
Result

          

🔒 Patterns and test text never leave your browser.

Quick reference

\d digit · \w word · \s space
. any · ^ start · $ end
* 0+ · + 1+ · ? 0/1 · {2,5} range
[abc] set · [^a] not · (a|b) or
(...) group · (?<n>...) named · (?:...) non-capturing
\b word boundary · \\ escape

Which regex flavor you are actually testing

This tester runs your pattern through the browser's own RegExp engine, so what you see is ECMAScript behaviour exactly as Node.js and every modern browser will execute it. That matters more than it sounds, because "regex" is not one language. A pattern copied from a Python tutorial, a grep man page or a Java codebase can be valid in all of them and still match different things.

The differences that bite most often are the ones ECMAScript does not have. There are no atomic groups (?>...) and no possessive quantifiers a*+, both of which PCRE users reach for to control backtracking. Recursion (?R) and subroutine calls are absent, so genuinely nested structures — balanced brackets, nested JSON — cannot be matched with a JavaScript regex at all, no matter how clever the pattern. Comments with (?#...) and the /x extended-whitespace mode do not exist either, which is why long JavaScript patterns are so hard to read.

Lookbehind is the reverse case: (?<=...) and (?<!...) were added in ES2018 and, unusually, JavaScript's implementation is unbounded — it accepts variable-length lookbehind such as (?<=a+), which PCRE and Python's re both reject. Patterns written here can therefore fail when ported the other way.

The g flag keeps state, and it causes real bugs

A regex literal with g or y is not a stateless description of a pattern. It carries a mutable lastIndex property, and .test() and .exec() both advance it. Call .test() twice on the same string with the same global regex and the second call can return false, because matching resumed from where the first one stopped.

This turns into an intermittent bug the moment a global regex is stored in a module constant or a loop variable and reused across inputs — roughly half the values get tested from the wrong offset. The fixes are to drop the g flag when you only need a boolean, construct the regex inside the loop, reset lastIndex to 0 yourself, or use String.prototype.match and matchAll, which handle the bookkeeping. Because this tool re-runs the pattern from a clean state on every keystroke, it will never reproduce the bug for you — worth knowing before you conclude a pattern is fine.

Catastrophic backtracking and how to spot it here

The one failure mode that turns a regex into a security problem is catastrophic backtracking. When a quantifier is nested inside another quantifier and both can match the same characters — the classic shapes are (a+)+, (a|a)* and (\s*.*)* — the number of ways the engine can split the input grows exponentially with its length. A pattern that returns instantly on twenty characters can hang for minutes on forty.

On a server this is a denial-of-service vector known as ReDoS, because JavaScript's regex engine is synchronous and blocks the event loop: one crafted request can freeze the whole process. The tell is a pattern where two adjacent constructs can both consume the same character, followed by something that can fail — the failure is what forces the engine to try every split.

You can surface it in this tool by lengthening your test string rather than by staring at the pattern. Paste thirty or forty repetitions of the character your quantifiers overlap on, ending with one character that cannot match, and watch whether highlighting stalls. If it does, rewrite the pattern so the alternatives are mutually exclusive, anchor it, or replace the inner quantifier with a specific character class. For simple structured formats, a hand-written parser is often both faster and safer than the regex it replaces.

Regex tester FAQ

What regex flavor is this?

JavaScript (ECMAScript), with flags g, i, m, s, u and y — exactly what runs in browsers and Node.js.

Does it show capture groups?

Yes — each match lists its numbered and named groups with positions, and matches highlight live.

Is my text private?

Completely — everything runs in your browser; nothing is uploaded.

Why does my pattern work here but fail in Python or PCRE?

The most common causes are lookbehind and escaping. JavaScript allows variable-length lookbehind that Python's re rejects, and JavaScript has no atomic groups or possessive quantifiers, so a PCRE pattern using them will not even compile here. Character-class shorthands also differ: \d is ASCII-only in JavaScript unless you add the u flag, while Python 3 matches Unicode digits by default.

Why did my regex suddenly stop matching in my own code?

If the regex has the g flag and you reuse the same object, its lastIndex persists between calls, so matching restarts partway through the next string. Either remove g, reset lastIndex = 0 before each use, or create the regex fresh each time.