Regex Tester
Test JavaScript regular expressions live, see every match highlighted in your test string, plus capture groups.
About the Regex Tester
Enter a pattern with flags, paste in a test string, and matches light up live using real <mark> elements positioned from the match indices rather than injecting your input as raw HTML. Global matching, capture groups, and named groups are all supported, and zero-length matches are handled correctly instead of locking up the tab.
Why the tool switches between matchAll() and a single exec() call
JavaScript's String.matchAll() method, which returns every match at once, specifically requires the regex to have the global (g) flag set, and throws a TypeError if it doesn't. This tool checks for that flag and branches accordingly: with g present, it uses matchAll() to collect every match in the string; without it, it falls back to a single RegExp.exec() call, which only ever returns the first match, exactly mirroring how these two methods actually behave in real JavaScript code. That's also a useful thing to internalize while testing, forgetting the g flag is one of the most common reasons a "why is my regex only matching once" bug happens in real projects.
The historical gotcha behind zero-length match handling
Patterns that can match an empty string, like x* or a lookahead-only pattern, are notoriously easy to get wrong in a manual regex loop: older code that repeatedly called .exec() while manually tracking lastIndex could get stuck reporting the same zero-length match forever if the position never advanced, an infinite loop that would genuinely freeze a browser tab. Modern matchAll() already solves this at the language specification level, but this tool's own highlighting loop adds a second layer of defense on top, explicitly advancing its internal cursor forward even when a match has zero length, so the highlighting logic itself can never stall regardless of how the underlying match data is structured.
What each allowed flag actually does
The six flags this tool accepts map directly to JavaScript's own regex flags: g (global) finds every match instead of stopping at the first, i (ignoreCase) makes matching case-insensitive, m (multiline) makes ^ and $ match the start and end of each line rather than only the whole string, s (dotAll) lets . match newline characters too, u (unicode) enables full Unicode-aware pattern matching, and y (sticky) anchors matching to exactly the current position rather than searching forward. Typing any other letter gets rejected immediately with a specific error message before the pattern is even compiled, rather than letting the raw, more cryptic native constructor error surface instead.
Why capture groups and named groups get separate display sections
Plain capture groups, created with parentheses like (\d+), show up as numbered entries (Group 1, Group 2, and so on) accessible by index on the match array. Named groups, a newer addition using (?<label>...) syntax, are a structurally different feature, they populate a separate .groups object on the match keyed by whatever label you chose rather than by position. Since these are genuinely two different mechanisms in the language, each with its own way of being accessed in real code, this tool displays them as two visually distinct sections underneath each match rather than merging them into one ambiguous list.
Why the highlighted output can't be tricked by HTML-like test strings
A regex tester is exactly the kind of tool where people paste in genuinely messy real-world content to test against, log files, scraped HTML, user-submitted text, any of which might contain characters that look like markup. Building the highlighted result from real DOM text nodes and <mark> elements via textContent, rather than assembling an HTML string and injecting it, means a test string containing something like a literal <script> tag is rendered as inert, visible text exactly as typed, never interpreted or executed as markup.
Frequently Asked Questions
Why does my regex only find one match unless I add the g flag?
Without the global flag, this tool uses RegExp.exec(), which only returns the first match, exactly matching real JavaScript behavior. With the g flag, it switches to String.matchAll(), which requires that flag and returns every match in the string.
Can a zero-length match pattern freeze this tool?
No, even though zero-length matches historically caused infinite loops in manually written regex code, this tool's highlighting loop explicitly advances its position forward on every match regardless of length, on top of the built-in protection modern matchAll() already provides at the language level.
What do the g, i, m, s, u, and y flags each do?
g finds all matches, i ignores case, m makes ^ and $ match line boundaries instead of just the whole string, s lets . match newlines, u enables full Unicode matching, and y anchors matching to the exact current position. Any other letter is rejected immediately with a clear error.
What's the difference between numbered capture groups and named groups?
Numbered groups, made with plain parentheses, are accessed by position on the match array (Group 1, Group 2). Named groups, using (?<label>...) syntax, populate a separate .groups object keyed by name instead of position, a structurally different feature displayed in its own section.
Is it safe to test a regex against text containing HTML-like content?
Yes, the highlighted output is built from real DOM text nodes and mark elements using textContent, never by injecting an HTML string. A test string containing something like a script tag renders as plain visible text, it's never interpreted as executable markup.
What happens if I type an invalid regex pattern?
The pattern compilation is wrapped in error handling, so an invalid pattern shows the JavaScript engine's own descriptive error message instead of breaking the page or silently failing to update the results.