About Regex Tester
Test regular expressions with live match highlighting and capture groups. Free, no sign-up required.
How to use
- Type a regex pattern into the Pattern field — bare expression, no surrounding slashes (the tool adds them internally). Example: \d{3}-\d{4} for North American 7-digit phone tails. Backslashes are written single, not doubled.
- Set the Flags field to control match behavior. Default is g (global, find every match). Add i for case-insensitive, m for multiline (^ and $ match line boundaries), s for dotall (. matches newlines), u for full Unicode, y for sticky positioning.
- Paste your test text into the Test String textarea. Matches highlight in real time as you type either field — there is no Run button, every keystroke re-runs the regex against the entire test string.
- The Results panel below the test string shows each match with its position offset and any capture groups. Numbered groups (group 1, group 2) come from parenthesized sub-patterns; named groups (?\d{4}) appear by name.
- If your pattern is invalid, the tool shows the JavaScript engine's error message verbatim ('Invalid regular expression: ...'). The most common errors are unbalanced parentheses, dangling quantifiers, and bad character class ranges.
- Remember that this tool runs the JavaScript regex engine — flavors differ. PCRE (PHP/Python re), .NET, Go's RE2, and Java each have their own quirks. A pattern that works here will work in JS/TypeScript, but may need adjustment for other languages.
Examples
Email-shaped match
Pattern: [\w.+-]+@[\w-]+\.[\w.-]+. Flags: gi. Tests against 'Contact alice@example.com or bob+spam@sub.example.co.uk'. Both addresses match — including the + alias and the multi-part TLD. Note: this is a pragmatic regex, not a full RFC 5322 validator (which is hundreds of characters).
Named capture for date parts
Pattern: (?\d{4})-(?\d{2})-(?\d{2}). Flags: g. Match against '2026-04-30 was today'. Result: full match with named groups year=2026, month=04, day=30. Access in code via match.groups.year. Named groups beat numbered when patterns get long.
Frequently asked questions
What do all the regex flags actually do?
g (global): find every match instead of just the first. i: case-insensitive (a matches A). m (multiline): ^ and $ match start/end of each line, not just the whole string. s (dotall): . matches newlines too (default behavior excludes \n). u (unicode): treat the pattern as Unicode-aware, enables \p{} property classes. y (sticky): match must start exactly at lastIndex; useful for tokenizers. d (indices): adds position info to match results. Combine flags by concatenating: gim, gsu, etc.
Why does my regex work in Python but not in JavaScript (or vice versa)?
Different engines, different rules. JS lacks PCRE's possessive quantifiers (a++) and atomic groups ((?>...)). Python's re lacks lookbehind length flexibility that PCRE has (and JS only added variable-length lookbehind in 2020). PCRE supports recursion ((?R)), JS does not. Character class shortcuts differ — \w matches [A-Za-z0-9_] in JS but is locale-aware in some engines. For maximum portability, stick to a common subset and test in the actual target engine.
What is the difference between greedy, lazy, and possessive quantifiers?
Greedy (the default: *, +, ?, {n,m}) matches as much as possible while still allowing the whole pattern to succeed — it backtracks if needed. Lazy (*?, +?, ??, {n,m}?) matches as little as possible, expanding only when forced. Possessive (*+, ++ — not in JS) is greedy but never backtracks, which prevents catastrophic backtracking but can cause matches to fail where greedy would succeed. JS has no possessive; emulate with atomic groups via (?=(...))\1 trick.
Why is my regex catastrophically slow on certain inputs?
Catastrophic backtracking. Patterns like (a+)+ on input 'aaaaab' force the engine to try every possible split — exponential in input length. Common culprits: nested quantifiers ((\w+)+), alternations with overlap ((a|a)*), and unbounded lookarounds. Fixes: use possessive quantifiers (in PCRE), use atomic groups, anchor with ^/$ to limit search space, or rewrite to avoid the ambiguity. Test with adversarial inputs (long strings of nearly-matching chars) to catch this in dev.
How do lookahead and lookbehind work, and what do browsers support?
Lookahead (?=...) asserts what follows without consuming it; (?!...) is negative. Lookbehind (?<=...) asserts what precedes; (?
What is the difference between numbered and named capture groups?
Numbered: (\d+) becomes group 1, (\w+) becomes group 2, etc. Access via match[1], match[2]. Named: (?\d+) is accessed via match.groups.digits. Named groups are self-documenting and survive pattern edits — adding a group at position 1 does not shift all your numbered references. Both can coexist; named groups also count in the numbered sequence. For complex patterns, always prefer named — your future self will thank you.
What does \b actually match?
Word boundary — a zero-width position between a word character (\w: [A-Za-z0-9_]) and a non-word character (or start/end of string). \bcat\b matches 'cat' in 'a cat sat' but not in 'category'. The dependency on \w means it does not respect Unicode by default; with the u flag and \p{Letter} classes you get smarter boundaries. \B is the negation — non-word-boundary, useful for matching mid-word patterns.
Part of ToolFluency’s library of free online tools for Developer Tools. No account needed, no data leaves your device.