Regex Tester
Test a regular expression against sample text and see every match with its position and capture groups in a table. Uses the native JavaScript engine.
Worked examples
- Every number in a sentence
The everyday case: testing a pattern against real text and seeing every match with its position, not just the first one.
- Capturing groups for an email address
Case-insensitive matching with capturing groups is the most common real use — checking both that the pattern matches and what each group actually captured.
- An invalid pattern
An unterminated character class is a common typo — the exact JavaScript engine error is surfaced instead of a silent empty result.
What this tool does
This tool tests a regular expression against sample text using the browser's native JavaScript engine, and shows every match in a table with its position in the text, the matched substring, and the contents of any capturing groups. It's built for checking that a pattern actually does what you intend before dropping it into application code.
When you need it
- Verifying a validation pattern — for a form field, an API parameter, a config value — against a handful of real inputs before shipping it.
- Extracting structured pieces of text, like dates, IDs or key-value pairs, from log lines or free-form text, and confirming the capture groups line up correctly.
- Debugging why a regex isn't matching what you expect, by seeing exactly where it does and doesn't match.
- Learning regex syntax interactively, by tweaking a pattern and watching the match table update.
Reading the match table
Each row shows the zero-based index (counted in UTF-16 code units, so an emoji counts as two) where the match starts, the full matched text, and a summary of any capturing groups in the order they appear in the pattern. A group that didn't participate in a particular match — for example, one that's only reachable through an unmatched branch of an alternation — is labeled "unmatched" rather than shown as an empty string, so it's clear the group genuinely didn't capture anything rather than capturing empty text.
Why matching is always global
Building a table of every match requires scanning the whole input, so this tool always iterates as if the g flag were set, regardless of whether you included it yourself. The other flags you choose — i for case-insensitive, m for multiline anchors, s for dot matching newlines, u for full Unicode mode, y for sticky matching — still apply exactly as they would in your own code; only the "stop after the first match" behavior of a pattern without g is overridden, since a single-match table wouldn't be very useful for testing.
Guarding against runaway patterns
Certain regex patterns — particularly ones with nested repetition like (a+)+b — can take exponentially longer to fail than to succeed, a problem known as catastrophic backtracking or ReDoS. JavaScript cannot interrupt a regex that is already running, so this tool refuses the most recognisable catastrophic shape up front: a group that contains an unbounded quantifier and is itself repeated, such as (a+)+ or (\w+\s?)*. You get an error explaining why instead of a frozen tab. This check is a heuristic: it also rejects some harmless patterns of the same shape, and it does not catch every slow pattern (overlapping alternations like (a|aa)+ can still be slow). Test text is capped at 5,000 characters and the match loop stops after 1,000 matches, which limits output size but does not by itself stop backtracking. If a pattern seems to hang even on short text, rewrite it.
Limits
This uses the standard JavaScript RegExp engine, which differs in a few details from PCRE, Python's re module or other languages' regex flavors — most notably around lookbehind support in older engines and some Unicode property escapes. A pattern that works here should work in almost any modern JavaScript environment, but isn't guaranteed to be byte-for-byte portable to every other language.
Frequently asked questions
- Which regex engine does this use?
- The browser's native JavaScript RegExp engine — the same one that runs your pattern in application code, so results match what you'd see in a Node.js or browser script.
- Why are all matches shown even without the g flag?
- Building a matches table requires iterating over the whole text, so matching is always global internally. The i, m, s, u and y flags you choose still apply to how each match is found.
- Is there a limit on input size?
- Yes — test text is capped at 5,000 characters and matching stops after 1,000 matches. Obvious catastrophic-backtracking shapes such as (a+)+ are refused with an error; other slow patterns can still make the page unresponsive, so keep test text small when experimenting.
- How do unmatched optional groups appear?
- A capturing group that didn't participate in a match (for example inside an unmatched alternative) is labeled "unmatched" instead of showing an empty string, so it's distinguishable from a group that matched empty text.