Regex Tester
Test JavaScript regular expressions with flags, match highlighting, and replacement preview.
About the Regex Tester
The Regular Expression Tester on HT99 Tools lets you prototype a JavaScript regex against sample input and see exactly what matches, what capture groups return, and what a replace or split would produce. Type a pattern, choose your flags, paste a test string, and the tool tells you in milliseconds whether your regex is right before you commit it to source code.
Regular expressions are a compact DSL for matching patterns in text. They power search-and-replace in editors, route matching in Express and Next.js, input validation in forms, log parsing in Datadog and Splunk, and tokenisation in compilers. They are also famously write-only: a 30-character regex that works perfectly is also a 30-character regex that nobody can read six months later. Testing against real input before deployment is the only reliable way to catch the off-by-one quantifier or the greedy match that eats your delimiter.
This tool speaks ECMAScript regex (the dialect implemented by new RegExp in every browser). It supports lookahead and named groups (ES2018+), Unicode property escapes (ES2018+), and the dotAll flag (ES2018+). It does not support lookbehind in older browsers, though modern Chrome, Firefox, and Safari do.
How It Works
The pattern and flags are passed to new RegExp(pattern, flags). The five commonly useful flags are: g (global — find every match, not just the first), i (case-insensitive), m (multiline — ^ and $ match at line boundaries, not just string boundaries), s (dotAll — . matches newlines as well), and u (Unicode — treat the pattern as UTF-16 code points, enabling \u{1F600} escapes).
Match mode calls text.match(re) when the g flag is set (returning an array of every matched substring), or re.exec(text) when it is not (returning the first match with its capture groups). Replace mode calls text.replace(re, replacement), where replacement can reference capture groups with $1, $2, etc. Split mode calls text.split(re) and returns every substring that lies between matches.
If the pattern is invalid (unbalanced parentheses, unknown escape, dangling quantifier), the RegExp constructor throws SyntaxError with a line and column. The tool surfaces that message verbatim so you can fix the pattern without leaving the page.
Worked Examples
The default pattern \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b with flags gi matches the two email addresses in the test string (ada@example.com and grace@ht99.icu) and ignores the three malformed inputs (not.an.email, @missing.com, user@.org). The tool reports 2 matches and shows each one.
Switch the action to Replace with the default replacement [REDACTED] and the tool calls text.replace(re, '[REDACTED]'), producing Contact [REDACTED] or [REDACTED] for support. — exactly what you would do before logging user input to remove PII.
Try a capture-group example: pattern (\w+)\s+(\w+) with no flags, action Match. The single match returns three entries: group 0 is the whole match (Contact ada), group 1 is the first word (Contact), and group 2 is the second (ada). That is how you would extract first and last names from a sentence.
When to Use This Tool
- Validating form input (email, phone, postal code, credit card BIN) before sending to the server.
- Prototyping route patterns for Express, Next.js, or Django before pasting them into source.
- Writing log-parsing patterns for Datadog, Splunk, or
grep -E. - Sanitising user input before logging (replace email addresses or credit card numbers with placeholders).
- Testing regex-based find-and-replace operations in a text editor or IDE macro.
- Debugging a pattern that works in your unit tests but fails in production (paste the production input).
- Learning capture groups, lookahead, and named groups by experimenting with sample input.
Limitations & Disclaimer
The tool uses ECMAScript regular expressions (the dialect of new RegExp), not PCRE, RE2, or Python's re. ECMAScript lacks lookbehind in older browsers, recursive patterns, conditional backreferences, possessive quantifiers, the \K reset, and atomic groups. The tool does not flag catastrophic backtracking patterns (e.g. (a+)+b against aaaaaaaaaaaaaaaa); a poorly written regex can hang the tab for seconds. For untrusted input in production, prefer RE2 (constant-time, no backtracking) over JavaScript regex. See our disclaimer for full terms.
Frequently Asked Questions
What is the difference between match() and exec()?
<code>String.prototype.match(re)</code> with the <code>g</code> flag returns an array of every matched substring (no capture group detail). Without <code>g</code>, it returns the first match with capture groups. <code>RegExp.prototype.exec(text)</code> always returns the first match with capture groups; called repeatedly with the same regex object, it advances through the string. Use <code>matchAll</code> (ES2020) to iterate every match with capture groups in one pass.
What does the s (dotAll) flag do?
By default, <code>.</code> matches any character except a newline. The <code>s</code> flag (ES2018) makes <code>.</code> match newlines as well. This is useful when parsing multi-line blocks (HTML, JSON pretty-printed logs) where the content of a tag or string spans multiple lines.
Why does my regex work in regex101 but not here?
regex101 defaults to PCRE (PHP) flavour, which supports features ECMAScript does not: recursive patterns, conditional backreferences, possessive quantifiers, and the <code>\K</code> reset. ECMAScript regex is closer to PCRE Lite. If your pattern uses any PCRE-specific feature, simplify it for browser use or run it server-side with a PCRE library.
How do named capture groups work?
ES2018 added named captures: <code>(?<year>\d{4})-(?<month>\d{2})</code>. The group is accessible as <code>match.groups.year</code> and <code>match.groups.month</code>, or as <code>$<year></code> in a replacement string. Named groups are far more readable than numbered groups for non-trivial patterns.
Why is my regex matching too much (greedy matching)?
Quantifiers (<code>*</code>, <code>+</code>, <code>?</code>, <code>{n,m}</code>) are greedy by default — they match as much as possible. Append <code>?</code> to make them lazy: <code>.*?</code> matches as little as possible. For matching HTML tags, <code><.*?></code> stops at the first <code>></code>; <code><.*></code> matches all the way to the last <code>></code> in the string.
Is my input uploaded anywhere?
No. All matching runs in the browser. Patterns, sample strings, and PII you redact never leave the device.
Last updated: September 9, 2026 · Author: HT99 Tools Editorial Team