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.

toolkit.codes/regex-tester
g
Regex flags
Type a pattern to test it
#IndexMatchGroups
UTF-8
Ready
100% LOCAL

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Pattern (flags gi)
(?<user>[\w.+-]+)@(?<domain>[\w-]+(?:\.[\w-]+)+)
On the sample text
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.

Pattern (no g)
^[A-Z]{2}-\d{4}$
Behaviour
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.

Pattern + replacement
(?<last>\w+), (?<first>\w+)
→ $<first> $<last>
Result (g)
Lovelace, Ada → Ada Lovelace
Hopper, Grace → Grace Hopper

Find doubled words

A backreference matches what a group captured — the classic proofreading pattern.

Pattern (flags gi)
\b(\w+)\s+\1\b
On real text
"the the" and "is is" flagged;
"the theory" untouched

Regex syntax cheat sheet (JavaScript)

CategoryTokenMeaning
Classes.Any character except newline (add s to include it)
\d · \DDigit 0-9 · anything else
\w · \WWord character [A-Za-z0-9_] · anything else
\s · \SWhitespace (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 · \BWord 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|bAlternation — a or b
Lookarounds(?=…) · (?!…)Lookahead: followed by · not followed by
(?<=…) · (?<!…)Lookbehind: preceded by · not preceded by
Flagsg i m s u yglobal · 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

BehaviourJavaScriptPython (re)JavaPCRE (Perl/PHP)
Named group(?<n>…)(?P<n>…)(?<n>…)Both spellings
Named ref in replacement$<n>\g<n>${n}${n} / \g{n}
LookbehindVariable-lengthFixed-width onlyBounded lengthFixed-width (or use \K)
\d \w meaningASCII alwaysUnicode by default (opt out: re.ASCII)ASCII by default (opt in: UNICODE_CHARACTER_CLASS)ASCII by default (opt in: (*UCP))
Flagsg i m s u y lettersre.I re.M re.S re.X constants; no g — use findallPattern.CASE_INSENSITIVE… or inline (?i)Delimiter letters /i /m /s /x
Escaping in source codeRegex literal — /\d+/ as writtenRaw 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.