Test a regular expression against any text and see every match, its position, and its capture groups. Toggle flags live — all in your browser, using the real JavaScript engine.
This regex tester runs a pattern against a block of text and shows you exactly what it matched — the matched substring, the offset it started at, and the contents of every capture group, including named ones. Flags can be typed directly or toggled with buttons, so you can see how a single letter changes everything: adding `g` turns one match into all of them, `i` collapses the distinction between `Error` and `error`, and `m` moves `^` and `$` from the ends of the string to the ends of each line.
The engine here is the one already in your browser, which matters more than it sounds. A regex is not a universal language — JavaScript, PCRE, Python, Go, and grep all disagree about lookbehind, atomic groups, possessive quantifiers, and Unicode property escapes. Testing a pattern in a PHP-flavoured playground and then shipping it in a JavaScript file is a reliable way to lose an afternoon. Because this tool calls `RegExp.prototype.exec` directly, whatever you see here is what your application will see too.
There is a safety net worth knowing about. Certain patterns — nested quantifiers like `(a+)+$` are the classic example — backtrack exponentially and can lock up a tab on inputs of only a few dozen characters, the same failure mode behind real ReDoS outages. This tool caps the number of matches it will collect and tells you when it stopped early, and it guards the global match loop against zero-length matches that would otherwise spin forever. It cannot make a pathological pattern fast, so if the page hesitates, treat that as a finding rather than a bug. Everything runs locally in your browser: no pattern and no test text is ever uploaded or stored.
`g` (global) finds every match instead of stopping at the first. `i` makes matching case-insensitive. `m` (multiline) makes `^` and `$` match at line breaks rather than only at the start and end of the whole string. `s` (dotAll) lets `.` match newline characters. `u` enables full Unicode mode, which is required for `\p{...}` property escapes and correct handling of characters outside the Basic Multilingual Plane. `y` (sticky) anchors each attempt to the current `lastIndex`, which is useful when writing tokenizers.
A greedy quantifier such as `.*` takes as much text as it can and then gives characters back until the rest of the pattern fits. A lazy one, written `.*?`, takes as little as possible and expands only when forced. The difference shows up immediately with HTML-ish input: `<.*>` against `<a><b>` matches the whole string, while `<.*?>` matches just `<a>`. Lazy quantifiers are usually what you want when matching between delimiters.
That is catastrophic backtracking. When a pattern contains a quantifier inside another quantifier — `(a+)+`, `(\s*\w+)*` — the engine can try an exponential number of ways to split the input before concluding there is no match. The fix is to remove the ambiguity: make inner parts non-overlapping, replace `.*` with a negated character class like `[^"]*`, and anchor the pattern so failure is detected early. The same class of bug is a real denial-of-service vector when a regex runs over user-supplied input on a server.
JavaScript's regex engine is genuinely different. It has no atomic groups, no possessive quantifiers, no recursion, no `\A` or `\z` anchors, no inline comments with `(?#...)`, and no free-spacing `x` flag. Named groups use `(?<name>...)` rather than PCRE's `(?P<name>...)`. Lookbehind is supported in modern browsers but was added late, so older runtimes may reject it. If you copied a pattern from a Stack Overflow answer written for PHP or Python, expect to adapt it.
Patterns that can match nothing — `a*`, `\b`, `(?:)` — succeed at every position in the string, including the position past the last character. With the global flag that produces one empty match per character, which is why a zero-length match must be stepped past manually or the loop never advances. If you are seeing a stream of empty results, make the pattern require at least one character: `a+` instead of `a*`.