How live match highlighting works
Paste a block of log output, type a pattern, and every match lights up before you finish the
thought. The tester runs your pattern through the browser's native RegExp engine on a short
debounce, so feedback arrives as you type instead of after a button press. Highlights render in
an overlay that sits pixel-aligned behind the text you are editing, which means you keep your
cursor, your selection, and your scroll position while you experiment.
How capture groups are tinted
Capture groups get their own treatment. Behind the scenes, the tool compiles every pattern with
the hasIndices flag, which makes the engine report the exact start and end offsets of each group
rather than forcing the tool to guess by re-searching substrings.1
Each group receives one of five rotating tints inside the wider match highlight, and the inspector
beside the editor lists the active match with every group's number, name, text, and character range.
A concrete pattern makes the group inspector easier to picture. Typing (\d{3})-(\d{4}) against
the test string Call 555-1234 now highlights 555-1234 as the full match, with
group 1 capturing 555 and group 2 capturing 1234. The inspector lists both
groups with their exact character offsets the moment the pattern compiles, so you can confirm the split
is landing where you expect before wiring the pattern into real code.
Both forms of parenthesis group. A plain (...) also captures: it takes a numbered
slot in the inspector and a $1 reference in Replace mode, while
(?:...) groups without capturing, so alternation or a quantifier can wrap a
subexpression without consuming a group slot.2 The
difference is visible the moment you edit it: swap a (...) for (?:...)
and the group's inspector row disappears, while every later group renumbers, since group
numbers follow the order of the opening parentheses. Use the capturing form when the
inspector or a $1 reference will actually read the text back; for structure
alone, the non-capturing form keeps your numbering exactly as planned.
Matching special characters literally
Every metacharacter in a pattern is doing a job. The dot matches almost any character, the
question mark makes an item optional, and brackets open a character class, so a literal one
needs the backslash: \. matches a period, \? a question mark, and
\[ an opening bracket.3 In a string literal, the
pattern needs one more step, because the backslash is also an escape in JavaScript strings
and has to be doubled inside the string.4 The
Explain panel turns that into a fast check, since it names every escaped literal one by one
as it walks your pattern, and a paste that lost its backslashes shows up there before it
costs you an hour of debugging.
What each flag does
The five toggles beside the pattern field each change one engine behavior. With
g on, matching finds every occurrence instead of stopping at the first, and
g is also what makes Replace rewrite every occurrence rather than just the
leading one.3 The i toggle ignores letter case,
so ERROR and error become interchangeable. Under m,
the ^ and $ anchors move from the boundaries of the whole input
to the start and end of each line, the shape you want when a pattern targets individual
log lines.5 The s toggle changes something else
entirely: it lets the dot match line breaks. The two are the classic mix-up. Multiline
text often needs both, because each flag changes a different thing.
The u toggle is the deepest of the five. It switches matching into Unicode
mode, and its biggest payoff is unlocking \p{...} property escapes,
so a class like \p{L} can match a letter in any script instead of
one alphabet at a time.5 Most everyday patterns run the same
with it off, and the toggle ships that way by default. The closing point is practical:
flipping any toggle re-evaluates the pattern on the spot, so you can paste a multiline
block with m off, switch it on, and watch the highlight show exactly which
anchors moved.
Why some patterns freeze other testers
Some patterns can freeze a browser tab solid. A regular expression like (a+)+$ tested against a
long run of the letter a forces the engine to try an exponential number of ways to split the
input, a failure mode called catastrophic backtracking; security literature names the attack that
exploits it ReDoS, short for regular expression denial of service.6
Most online testers run your pattern on the page's main thread, so one careless quantifier stops
the whole tab from responding.
How this tester contains runaway patterns
This tool treats that risk as a design requirement. Every evaluation runs inside a dedicated Web Worker, a separate thread that cannot block the page you are looking at.7 If the worker fails to answer within 500 milliseconds, the tool terminates the entire thread, spins up a fresh one for your next keystroke, and tells you which kind of pattern usually causes the blow-up.8 Your tab keeps scrolling the whole time.
This hard stop is what keeps the interface responsive. Once the worker is terminated it cannot resume, so the tester spins up a fresh worker for your next keystroke and hands the new evaluation to it. The main thread never waits on the doomed computation, which is why you can keep typing and scrolling while a runaway pattern is still being abandoned.
Find and replace with group references
Testing a match is only half the job, because most patterns exist to rewrite text. In Replace mode, the match highlighting stays visible on your test string while a second field accepts a replacement template, and the substituted result updates live underneath. You see what will be replaced and what it becomes at the same moment, which catches off-by-one group references before they reach your codebase.
The template follows the exact semantics of JavaScript's String.replace, so whatever works here
works unchanged in your code.9 Write $1 or $2 to
insert numbered capture groups, $<name> to insert a named group, $& for the whole
match, and $$ for a literal dollar sign. When the g flag is on, the engine rewrites every
occurrence; without it, only the first. Because replacement runs through the same worker and
timeout guard as matching, a pathological pattern cannot freeze the preview either.
By default, quantifiers are hungry. A .* or a .+ takes the longest
run it can, which is why a replacement built on .* so often swallows more of
the line than you intended; appending ? turns the quantifier lazy, so it stops
at the shortest match that still satisfies the pattern.10
Because the live highlight draws the consumed span the moment the character lands, you see
both behaviors side by side as you edit, and the Explain panel names the lazy variant
whenever one is present. If a find-and-replace eats a whole line instead of the fragment
you wanted, the fix is usually one character: that trailing ?.
Treat your pattern like code
A regex that works on one happy-path string is not finished. Unit Tests mode lets you pin the behavior down the way you would pin down a function: add a list of short test strings, mark each one as should match or should not match, and watch a live pass count while you refine the pattern. Negative cases matter as much as positive ones, since the most common regex bug is matching more than you intended.
Work through positive and negative cases
For an email pattern, for example, you might assert that [email protected] passes, that a bare word fails, and that a string with two @ signs fails. Every edit to the pattern re-runs all rows in a single batch, and each row shows an immediate PASS or FAIL badge next to its expectation. When you share the link, the whole suite travels with it, so a teammate opens not just your pattern but the evidence that it works.
The Snippets menu saves the blank-field moment. It holds eight curated starting points, one
click each into the pattern field: an email address, a URL, an IPv4 address, a phone
number, a YYYY-MM-DD date, a hex color, a URL slug, and leading or trailing
whitespace. Treat them as editable starting points rather than standards, because every one
of them makes judgment calls a stricter product decision might reject: the email pattern
accepts plus tags, the slug pattern insists on lowercase, and the date pattern does not
check the day against the month. The natural next step is the workflow this section
already describes: load one, then pin down its edge cases with test rows until the
pattern earns its PASS badges.
Share links without giving up privacy
Nothing you type here leaves your machine. Matching, replacement, unit tests, and the explainer all run inside your browser tab against JavaScript's built-in regular expression engine, and you can cut the network connection after the page loads to confirm it for yourself.9 There is no account, no server-side history, and no request that carries your pattern or test data anywhere.
Sharing works without giving that up. When you click Share, the tool serializes your pattern, flags, mode, test string, replacement template, and unit test rows into a compact encoded string, writes it into the page address, and copies the full link to your clipboard. The state lives entirely inside the URL, so it reaches exactly the people you send it to and nobody else. Until you press that button, nothing is encoded anywhere at all.
Pattern-Is-Working Checklist
- Capture groups match what you expect Check the inspector lists the right text and character range for every numbered or named group, not just the overall match.
- No "pattern took too long" warning That message means catastrophic backtracking — nested or overlapping quantifiers such as (a+)+ are the usual cause.
- Flags match your real code A pattern tested with g or i toggled on behaves differently once those flags are missing in your actual code.
- Negative test cases fail correctly A regex that only matches the happy path is unfinished — add strings that should not match in Unit Tests mode.
Run your own pattern and test text above and check it against this list before shipping it.
- 1.
MDN Web Docs, "RegExp.prototype.hasIndices," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices
- 2.
Jan Goyvaerts, "Regex Tutorial: Parentheses for Grouping and Capturing," regular-expressions.info, accessed September 2026. https://www.regular-expressions.info/brackets.html
- 3.
"Regular expression," Wikipedia, accessed July 2026. https://en.wikipedia.org/wiki/Regular_expression
- 4.
Ecma International, "ECMAScript® 2025 Language Specification (String Literals)," ECMA-262, ecma-international.org, June 2025. https://262.ecma-international.org/16.0/#sec-string-literals
- 5.
TC39, "s/dotAll flag for regular expressions," tc39.es, January 2018. https://tc39.es/proposal-regexp-dotall-flag/
- 6.
OWASP Foundation, "Regular expression Denial of Service - ReDoS," owasp.org, accessed July 2026. https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- 7.
WHATWG, "HTML Standard: Web workers," html.spec.whatwg.org, accessed July 2026. https://html.spec.whatwg.org/multipage/workers.html
- 8.
MDN Web Docs, "Worker: terminate() method," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/API/Worker/terminate
- 9.
Ecma International, "ECMAScript® 2026 Language Specification (String.prototype.replace)," ECMA-262, tc39.es, accessed July 2026. https://tc39.es/ecma262/#sec-string.prototype.replace
- 10.
Jan Goyvaerts, "Regular Expressions Quick Reference," regular-expressions.info, accessed September 2026. https://www.regular-expressions.info/refquick.html