Regex Tester_
A regex tester that runs JavaScript's own RegExp engine — the dialect is stated, not hidden — with live match highlighting, a table of every match and its numbered and named capture groups, and a replace preview that understands $1, $<name>, and $&. Flags are one click each: g, i, m, s, u, y.
A runaway pattern can't freeze this page: matching runs in a background worker with a one-second kill switch, so catastrophic backtracking gets reported instead of hanging your tab. Pattern and test text never leave your browser — nothing is saved server-side.
\p is a Unicode property only with the u flag — without it the pattern matches the literal text p{…}.
highlighting is off over 50k characters — the match table stays live
| # | Index | Match | Groups |
|---|
—
A JavaScript regex tester that says so
What engine actually runs your pattern
Every online regex tester executes some concrete engine, and the differences between engines are exactly where patterns break. This page is explicit: your pattern runs on the ECMAScript RegExp implementation of the browser you are using right now — the same engine your JavaScript and TypeScript code will use in production, so there is no translation step between what you test and what you ship. Testers that offer a flavor dropdown but evaluate everything in JavaScript quietly mislead Python and Java users; the ones that really run other engines do it server-side, which means your test data travels. Here the dialect is named, the differences are documented two tables down, and nothing you type leaves the page.
Reading the match table
Run the sample and you'll see each match as a row: the index where it starts (counted from 0), the full matched text, and every capture group. Numbered groups appear as $1, $2… in source order of their opening parentheses; named groups — (?<user>…) — show their names. A group that matched nothing shows as empty; one in a branch that never ran shows as undefined — a distinction that matters when your code checks match.groups.user. Zero-length matches are listed too (a* against "bb" legitimately matches three empty strings), and the highlighter marks their positions with a thin bar rather than pretending they don't exist.
The replace preview
The Replace tab feeds your pattern and a replacement template through JavaScript's native replace: $1 inserts a numbered group, $<name> a named one, $& the whole match, and $$ a literal dollar sign. The g flag decides scope — every match with it, only the first without — and the note above the result says which happened, so a "why did only one change?" bug is visible before it reaches code. The result is copyable in one click.
Porting patterns to Python, Java, C#, and Perl
Searching for a Python regex tester or a regex tester for Java and landing on a JavaScript engine deserves honesty: most patterns port cleanly, and the ones that don't fail in predictable places. Python spells named groups (?P<name>…) and restricts lookbehind to fixed width; Java double-escapes everything inside string literals ("\\d+") and keeps \w ASCII unless you ask; C# (.NET) allows variable-length lookbehind and Unicode-aware classes; Perl and PCRE accept several named-group spellings and add \K. Verify the JavaScript behaviour here, then check your language's row in the table below before pasting the pattern into code.
How to use
- 01Type the pattern between the slashes — matching runs on every keystroke, and a syntax error is reported under the box with the reason and approximate position.
- 02Toggle flags as pills: g finds every match instead of the first, i ignores case, m makes ^ and $ work per line, s lets . cross newlines, u enables Unicode property escapes, y anchors matching at the exact position. The echo after the closing slash shows the active set.
- 03Paste real data into the test string — matches highlight live as you type either field, and the table below lists index, match, and every capture group per row.
- 04Switch to Replace to preview a substitution with $1, $<name>, or $& references, then Copy_Result — the note says whether one match or all were replaced.
Four patterns people actually test
Extract emails with named groups
Pull addresses out of messy text, keeping user and domain separate — the Sample button loads this.
(?<user>[\w.+-]+)@(?<domain>[\w-]+(?:\.[\w-]+)+)
3 matches; groups user=ada.l domain=example.dev …
Validate one whole input
Anchors make validation exact: without ^ and $, an ID pattern happily matches inside garbage.
^[A-Z]{2}-\d{4}$AB-1234 → 1 match xxAB-1234yy → 0 matches
Swap name order with replace
Named references make the replacement readable — and survive reordering the groups later.
(?<last>\w+), (?<first>\w+) → $<first> $<last>
Lovelace, Ada → Ada Lovelace Hopper, Grace → Grace Hopper
Find doubled words
A backreference matches what a group captured — the classic proofreading pattern.
\b(\w+)\s+\1\b
"the the" and "is is" flagged; "the theory" untouched
Regex syntax cheat sheet (JavaScript)
| Category | Token | Meaning |
|---|---|---|
| Classes | . | Any character except newline (add s to include it) |
\d · \D | Digit 0-9 · anything else | |
\w · \W | Word character [A-Za-z0-9_] · anything else | |
\s · \S | Whitespace (space, tab, newline…) · anything else | |
[abc] · [^abc] · [a-z] | One of · none of · range | |
\p{Script=Greek} | Unicode property — requires the u flag | |
| Quantifiers | * · + · ? | 0 or more · 1 or more · 0 or 1 (all greedy) |
{3} · {2,} · {2,5} | Exactly 3 · at least 2 · between 2 and 5 | |
*? · +? · ?? | Lazy versions — match as little as possible | |
| Anchors | ^ · $ | Start · end of string (of each line with m) |
\b · \B | Word boundary · not a word boundary | |
| Groups | (…) · (?:…) | Capturing · non-capturing |
(?<name>…) | Named capturing group | |
\1 · \k<name> | Backreference to a group, by number or name | |
a|b | Alternation — a or b | |
| Lookarounds | (?=…) · (?!…) | Lookahead: followed by · not followed by |
(?<=…) · (?<!…) | Lookbehind: preceded by · not preceded by | |
| Flags | g i m s u y | global · ignore case · multiline ^$ · dotAll · unicode · sticky |
Everything above is the JavaScript dialect this page runs; lookbehind and named groups are safe in every current browser and Node.js.
JavaScript vs Python vs Java vs PCRE — the differences that break ports
| Behaviour | JavaScript | Python (re) | Java | PCRE (Perl/PHP) |
|---|---|---|---|---|
| Named group | (?<n>…) | (?P<n>…) | (?<n>…) | Both spellings |
| Named ref in replacement | $<n> | \g<n> | ${n} | ${n} / \g{n} |
| Lookbehind | Variable-length | Fixed-width only | Bounded length | Fixed-width (or use \K) |
\d \w meaning | ASCII always | Unicode by default (opt out: re.ASCII) | ASCII by default (opt in: UNICODE_CHARACTER_CLASS) | ASCII by default (opt in: (*UCP)) |
| Flags | g i m s u y letters | re.I re.M re.S re.X constants; no g — use findall | Pattern.CASE_INSENSITIVE… or inline (?i) | Delimiter letters /i /m /s /x |
| Escaping in source code | Regex literal — /\d+/ as written | Raw strings — r"\d+" | Double everything — "\\d+" | Depends on host language |
The pattern language is only half the port — the API around it (g flag vs findall, replace references) changes just as much.
Tips & best practices
- Build incrementally: start with the literal part, then add classes and quantifiers one at a time — the moment the highlight vanishes tells you which addition broke it.
- Use (?:…) when you only need grouping — every unnamed capture shifts the $1 numbering after it and silently breaks replacements.
- Test text that must NOT match, not just text that should — most regex bugs are over-matching, and only a negative sample exposes them.
- Anchor validations with ^ and $ (and skip g): an unanchored pattern happily matches inside garbage.
- Name your groups: $<first> $<last> survives reordering the pattern; $2 $1 does not.
- Keep u on for emoji or non-Latin text — without it, patterns operate on UTF-16 units and can split a character in half.
Gotchas — where regular expressions bite
Catastrophic backtracking can hang an app
Nested quantifiers over the same characters — (a+)+$ against a long run of a with no match at the end — force the engine to try every partition of the input: exponential time, frozen thread. This page kills the run after one second and tells you; production code has no such guard.
Greedy quantifiers eat more than you meant
<.+> against "<b>bold</b>" matches the whole string — .+ grabs everything and backtracks only as far as it must; the lazy <.+?> matches the two tags separately. When a match is mysteriously long, the fix is a ? after the quantifier or a negated class like [^>]+.
The g flag makes .test() stateful in code
A /g regex object keeps lastIndex between calls: re.test("abc") twice can return true then false on the same string. This page runs each keystroke fresh — but in code, reuse a g-flagged regex only with matchAll, or reset lastIndex yourself.
A pattern that works here can fail in another language
The usual suspects: (?P<name>…) vs (?<name>…), variable-length lookbehind (JavaScript and .NET yes, Python and PCRE no), and \d matching ٤ in Python but not in JavaScript. The differences table above makes a port a checklist, not a surprise.
String escaping is not regex escaping
In a JavaScript regex literal, \d is one backslash. Build the same pattern with new RegExp("…") and the string eats one level: "\\d" is required. Java doubles every pattern backslash. When a pattern that tested fine fails in code, count backslashes first.
Untrusted input plus an unbounded pattern is a denial of service
ReDoS is real: if user input reaches a regex with nested quantifiers, an attacker sends the exact string that triggers exponential backtracking. Validate length before matching, and treat the timeout here as a smoke test — if a pattern trips it on any input you can construct, do not deploy it.
Technical details
- Engine
- Your browser’s native ECMAScript RegExp (lookbehind, named groups, \p{…} with u) — the dialect JavaScript and TypeScript run
- Flags
- g global, i ignoreCase, m multiline, s dotAll, u unicode, y sticky — toggled as pills, echoed after the closing slash
- Runaway protection
- Matching runs in a Web Worker with a 1,000 ms kill timer; on timeout the worker is terminated and replaced, and the page reports likely catastrophic backtracking
- Match table
- Index, full match, numbered + named groups per row; shows the first 2,000 matches and reports the true total beyond that
- Highlighting
- Live overlay behind the test string; zero-length matches marked with a thin bar; disabled above 50,000 characters (matching itself stays live)
- Replace references
- $1…$99 numbered, $<name> named, $& whole match, $$ literal dollar; g decides first-vs-all and the note states which applied
- Error reporting
- Syntax errors show the engine’s reason (e.g. “Unterminated group”) plus an approximate position from a longest-valid-prefix probe
- Processing
- Pattern, flags, text, and replacement stay in your browser — nothing is uploaded or stored server-side, and there are no accounts
FAQ
Is this a JavaScript or PCRE regex tester?
JavaScript — the ECMAScript RegExp engine of your own browser, stated plainly rather than hidden behind a flavor menu. PCRE accepts syntax JavaScript rejects (possessive quantifiers, \K, recursion), so a PCRE-specific pattern may error here; the table above lists the differences that matter.
Can I test Python or Java regex here?
You can test the shared core — classes, quantifiers, anchors, groups, and lookarounds behave identically for most patterns. Before porting, check named-group spelling, lookbehind restrictions, \d/\w Unicode semantics, and backslash escaping in source; each has a row in the differences table.
Why does my pattern freeze other testers but not this one?
Patterns with nested quantifiers can backtrack exponentially — the engine is not stuck, it is trying billions of ways to fail. This page runs matching in a background worker and kills it after one second: you get a report instead of a frozen tab. Treat the report as a design warning.
How do I match across multiple lines?
Two flags, often confused: s makes . match newlines, so a.b can span a line break; m anchors ^ and $ at every line instead of only the string ends. Whole blocks usually want s; per-line validation wants m.
What does the u flag actually do?
It switches the engine to code-point mode: \p{…} property classes work, emoji count as single characters, and malformed escapes become errors instead of silently matching literals. Without u, \p{L} matches the literal text p{L} — the page shows a hint when that trap is active.
Can I save or share a pattern?
No — by design. Testers with pattern libraries and permalinks store your patterns (and often your test data) server-side; everything here stays in the page, so copy the pattern into your code or notes to keep it. For applying a finished pattern to documents repeatedly, a dedicated find-and-replace tool is the right home.