Regex Tester & Debugger

Type a pattern, see matches highlighted in your test text as you type — with every capture group tabled, a replace preview, and the pattern itself translated into plain English token by token. The matching engine is your browser's own JavaScript engine, run inside a guarded worker so even a catastrophic pattern can't freeze the tab. Nothing you paste leaves your machine.

Reading a regex in one line: a pattern is a sequence of things-to-match read left to right — literals match themselves, \d-style classes match one character of a kind, and quantifiers repeat whatever sits immediately before them: \d{4}-\d{2}-\d{2}  =  4 digits, dash, 2 digits, dash, 2 digits

Matching runs in your browser inside a sandboxed worker with a 1.5-second timeout, so a runaway pattern is cancelled instead of freezing the tab. Nothing you type is uploaded. The live engine is JavaScript (ECMAScript) — see the flavor chart below for how other languages differ.

Pattern explained, token by token

The same pattern from the box above, translated into plain English. Edit the pattern and this updates live — it's the fastest way to debug a regex you inherited.

[ Ad slot — replace with AdSense / Ezoic code ]

Pattern library — validated building blocks

Click to load a pattern into the tester. These are honest building blocks, not magic: each note says what the pattern deliberately doesn't cover, because a "regex generator" that hides its edge cases just moves the bug downstream.

Flavor cheat sheet: the same regex in seven engines

Regular expressions are a family of dialects, not one language — the pattern that works in your code review may fail here, or vice versa, for reasons that have nothing to do with your logic. This chart covers the differences that actually bite. The live engine on this page is JavaScript; read your language's column before copying a pattern across.

FeatureJavaScriptPython (re)JavaC# / .NETGo (RE2)PHP (PCRE)POSIX ERE
Lookbehind (?<=…)Yes (variable-length)Fixed-width onlyBounded-widthYes (variable)NoFixed-width branchesNo
\d matches non-ASCII digits?No — ASCII 0–9 only, even with uYes by default on str (use re.ASCII to restrict)No, unless UNICODE_CHARACTER_CLASSYes by defaultNo — ASCII onlyNo by default (/u + UCP changes it)Use [[:digit:]]
Named group syntax(?<name>…)(?P<name>…)(?<name>…)(?<name>…) or (?'name'…)(?P<name>…)Both stylesNone
Backreferences \1YesYesYesYesNo (by design)YesNot in ERE
Inline flags (?i)No — flags go after the closing /YesYesYesYesYesNo
Possessive / atomic groupsNo3.11+ onlyYesAtomic (?>…)N/A (never backtracks)YesNo
Can it blow up (backtracking)?YesYesYesYes (timeout API available)No — linear time, guaranteedYes (has limits)Depends on implementation

Two practical takeaways. First, if a pattern must run on untrusted input at scale, Go's RE2 semantics (also available as libraries elsewhere) are the only ones that guarantee no catastrophic backtracking — the trade being no backreferences or lookaround. Second, the most common porting failures are mundane: Python's (?P<name>) syntax rejected by JavaScript, an inline (?i) flag that JavaScript doesn't accept, or a \d that suddenly matches ٣ and 4 when the pattern moves to Python.

How the highlighting and groups work

The tester compiles your pattern with the browser's own RegExp, runs it against the test text, and walks the matches. With the g flag on, every match is found; off, only the first. Each numbered group is whatever its parentheses captured (or no match if that branch didn't participate), and named groups — (?<year>\d{4}) — appear under their names. The replace preview uses JavaScript's String.replace semantics: $1 for numbered groups, $<name> for named ones, $& for the whole match, and $$ for a literal dollar sign.

Frequently asked questions

My pattern works in Python — why not here?

Almost always one of four dialect differences. Python's named groups are (?P<name>…); JavaScript requires (?<name>…) without the P. Python accepts inline flags like (?i) anywhere; JavaScript takes flags only as the checkboxes here. Python's \A and \Z anchors don't exist in JavaScript (use ^ and $ without the m flag). And a pattern written with re.VERBOSE whitespace will match literal spaces here. The chart above covers the rest.

What is catastrophic backtracking, and what does the guard here do?

Patterns like (a+)+$ against a long string of a's followed by a b force the engine to try exponentially many ways of dividing the a's before concluding there's no match — seconds, then minutes, of CPU. It's the mechanism behind real-world ReDoS outages. This page runs every match inside a Web Worker with a 1.5-second timeout: a pathological pattern gets cancelled with a warning instead of freezing your tab, so you can experiment freely. The fix in your code is to remove nested quantifiers over overlapping character sets, anchor the pattern, or use a linear-time engine (see the Go column above).

Is the text I paste sent anywhere?

No. Compilation and matching happen in your browser — no server, no analytics on your pattern or text, and the page keeps working offline once loaded. Log excerpts and real emails are exactly the things you shouldn't paste into a random server-side tester.

Why does the email pattern reject some valid addresses?

Because every short email regex is a pragmatic approximation. The full grammar (RFC 5322) permits quoted local parts, comments, and other constructs no signup form wants; a regex that accepted them all would be pages long and still wrong about deliverability. The library pattern here checks the shape — something, @, something, dot, something — which is the right level for input validation. The only true validation is sending mail to it.

[ Ad slot — replace with AdSense / Ezoic code ]

Related tools