Regex Tester
Build and test a JavaScript regular expression against your own text, using the browser’s native RegExp engine so patterns behave exactly as they will in your code. Toggle the g, i, m, s, and u flags; inspect every match with its position and its numbered and named capture groups; and preview a replacement with $1, $&, and $<name> references. Zero-length matches are handled safely and terse engine errors are rewritten into plain language.
How Regex Tester works
Type a pattern and toggle the flags you need.
Paste the subject text to test it against.
Read the match list with positions and capture groups.
Turn on the replacement preview to try a substitution.
When to use Regex Tester
- Building a regular expression step by step against real sample text, watching the match count update as you refine the pattern.
- Inspecting exactly what each match captured — the whole match plus every numbered and named group — before you rely on the pattern in code.
- Turning on a specific flag combination such as global, ignore-case, and multiline, and seeing immediately how it changes the matches.
- Prototyping a replacement with $1, $&, or $<name> references and previewing the rewritten text before applying it anywhere.
- Decoding a cryptic engine error like “Nothing to repeat” into plain language that tells you what to fix.
- Testing a pattern that can match empty strings, such as a*, without the tester hanging, since zero-length matches are handled safely.
- Validating a pattern against sensitive data locally, since the subject text is matched in your browser and never uploaded.
Examples
Input
Pattern: (\w+)@(\w+) · Flags: g · Subject: “ada@example bob@site”
Output
Two matches. The first is “ada@example” at index 0, with $1 = “ada” and $2 = “example”; the second is “bob@site” at index 12, with $1 = “bob” and $2 = “site”. The Matches tile reads 2.
Each match lists its start index and its positional groups. The global flag is what lets the tester enumerate every match rather than stopping at the first.
Input
Pattern: (?<year>\d{4})-(?<month>\d{2}) · Replacement: “$<month>/$<year>” · Subject: “2026-09”Output
The match “2026-09” exposes year = “2026” and month = “09”, and the replacement preview renders “09/2026”.
Replacement uses native String.replace semantics, so $<name>, $1, and $& behave exactly as they do in JavaScript.
Input
Pattern: (unclosed
Output
Instead of the raw “Invalid regular expression: Unterminated group”, the tester shows “Unclosed group — you have a ‘(’ without a matching ‘)’.” The Matches tile shows a dash until the pattern compiles.
While the pattern is invalid, the subject text is left untouched and no matches are attempted.
How Regex Tester works under the hood
The tester compiles your pattern with the browser’s own JavaScript RegExp engine — the same engine your code will use if you paste the pattern into a Node or browser project — so what you see here is what you will get there. There is no separate dialect to learn or translate; features like lookbehind, named groups, and Unicode property escapes work exactly as your runtime supports them.
Five flags are exposed as toggles: g (global), i (ignore case), m (multiline, where ^ and $ match at line boundaries), s (dotall, where . also matches newlines), and u (Unicode). To enumerate every match rather than stopping at the first, the tester always compiles with the global flag internally, whether or not you have toggled g — so the g button chiefly affects how a replacement behaves, matching JavaScript’s own rules.
Each match is reported with its start index in the subject, the full matched text, its positional capture groups numbered from $1, and any named groups declared with (?<name>…). A group that did not participate in the match is shown as undefined rather than an empty string, which mirrors what your code would receive and helps you tell “matched nothing” apart from “matched an empty string”.
Zero-length matches are handled deliberately. A pattern like a*, \b, or (?=x) can match an empty string at a position without consuming any characters, which would loop forever under naive enumeration. After each empty match the engine advances its cursor by one, so the scan always makes progress. As a further guardrail, enumeration stops after 100,000 matches; the count you see is the number collected, and the rendered list is capped at the first 200 entries while the count itself stays exact.
The replacement preview runs through native String.replace, so the special tokens behave precisely as they do in JavaScript: $1 through $9 insert numbered groups, $<name> inserts a named group, $& inserts the whole match, and $$ inserts a literal dollar sign. The preview is only shown when you enable it, and an invalid pattern leaves the original subject in place rather than clearing it.
When a pattern fails to compile, the terse engine message is rewritten into something actionable — “Unterminated group” becomes “Unclosed group — you have a ‘(’ without a matching ‘)’”, “Nothing to repeat” explains that a quantifier has nothing before it, and so on. All of this — compilation, matching, and replacement — happens in your browser, so a pattern tested against sensitive log lines or personal data never leaves your device.
Common mistakes
- Assuming the g flag controls how many matches are listed. The tester enumerates all matches regardless, because it compiles with g internally; the g toggle mostly changes replacement behaviour, matching JavaScript’s rule that a non-global replace only touches the first match.
- Writing a pattern with delimiters, like /\d+/g. The field expects the bare pattern and separate flag toggles, so type \d+ and switch on g — the surrounding slashes would be matched literally.
- Confusing an undefined group with an empty match. A group shown as undefined never participated in the match; a group shown as (empty) matched a zero-length string. The distinction matters when you consume the groups in code.
- Expecting . to match a newline without the s flag, or ^ and $ to match at line breaks without the m flag. These are off by default, so a multiline subject often needs one or both toggled on.
- Being surprised that a pattern such as a* produces a match at every position. Zero-length matches are legitimate; the tester advances past each one so it does not hang, but the resulting count can be higher than you expected.
- Forgetting the u flag when using Unicode property escapes like \p{Letter}. Without u, that syntax is either an error or matches literally, depending on the pattern.