DevKitHub

Programming

Regex Tester — Match, Capture and Replace

Write a pattern and see what it matches, highlighted in place, with capture groups and an optional replacement. Patterns run in a worker that is killed if one takes too long.

2 lines

This tool runs entirely in your browser. Your input is never uploaded, stored or logged.

How it works

The pattern runs in a Web Worker rather than on the page, and that is the whole design. A regular expression is fast until it is not: a pattern like (a+)+$ against a few dozen characters can make the engine explore exponentially many ways to match, and the tab stops responding entirely — no scrolling, no typing, no closing the panel. Whether a given pattern will do this cannot be decided in advance; it is a property of the pattern and the input together. So the only real defence is to run it somewhere killable. If a run has not answered within a second, the worker is terminated and replaced, and you get told what happened instead of losing the page.

Matching itself handles the case that breaks most hand-written testers. A global pattern that can match the empty string does not advance the engine position on an empty match, so a plain exec loop returns the same match at the same index forever. Anything with a star, an anchor or a lookahead can trigger it. The position is stepped manually after every empty match, and the step is surrogate-aware so an emoji is never split down the middle and reported at a position that is not a character boundary.

Capture groups are shown with their names where the pattern uses them, and as undefined rather than empty where a group did not participate in the match — those are different things, and conflating them is a common source of confusion when reading results. Replacement uses your own flags, so a pattern without the global flag replaces once here exactly as it will in your code.

Common problems

Every example below is run against this tool in our test suite, so what it says here is what the tool actually does.

Invalid regular expression: unterminated group.

(abc
Why:
An opening parenthesis with no closing one. It usually means a literal bracket was meant, or a group was left half-written while editing the pattern.
Fix:
Close the group, or escape the parenthesis as \( if you meant to match one literally.

Range out of order in character class.

[z-a]
Why:
A character class range runs from a lower code point to a higher one. Writing it backwards is rejected rather than treated as an empty range, because it is always a mistake.
Fix:
Write the range in ascending order, as [a-z].

The browser froze and the tab had to be closed.

Why:
Catastrophic backtracking. A quantified group containing another quantifier, such as (a+)+, makes the engine try exponentially many ways to match before giving up, and on the main thread nothing else can run while it does.
Fix:
Cannot happen here — patterns run in a worker that is killed after a second. In your own code, avoid nesting quantifiers and prefer a possessive or atomic construct where your language has one.

Frequently asked questions

Can a bad pattern freeze the page?
No. Patterns run in a Web Worker and any run that has not finished within a second is terminated, with the worker replaced. You get a message saying the pattern was stopped. On a tester that runs patterns on the main thread, the same input takes the whole tab with it.
Which flavour of regex is this?
JavaScript, exactly as your browser implements it, including named groups, lookbehind and the unicode flags. It is the same engine your code will run, not a reimplementation that agrees with it most of the time.
Is my pattern or test text sent anywhere?
No. Everything runs in your browser, and there is deliberately no API endpoint for this tool — running a stranger’s regex against a stranger’s input on a shared server is a denial-of-service waiting to happen.
Why does a group show "(no match)" instead of being empty?
Because a group that did not participate in the match is undefined, which is a different thing from a group that matched an empty string. Treating them as the same is a common source of confusion when reading results back in code.

Last updated