Developer tool
Regex Tester
Test regular expressions with live highlighting, capture groups, and replace mode. Every match is counted, indexed and navigable, and nothing you type leaves your browser.
- Free
- Browser-based
- Live results
- No signup
- Regex highlighting
Enter a pattern to begin
//
Matches will appear here.
$& whole match
$1 group 1
$<name> named group
$$ literal dollar
$` before match
$' after match
The text being replaced, with every match still highlighted.
Shortcuts: Ctrl+Enter re-runs the pattern, Ctrl+Shift+F focuses the pattern box, Ctrl+Shift+R opens replace. With the cursor in the pattern box, Enter steps to the next match and Shift+Enter to the previous one.
Explain this pattern
Breaks your pattern into tokens and describes each one. Anything the breakdown cannot identify with confidence is marked unsupported rather than guessed at.
Example patterns
Each one loads a working pattern, its flags, sample text and a replacement. Treat them as starting points you adapt to your data, not as universal validation standards — real-world formats almost always have exceptions.
Regex cheat sheet
JavaScript RegExp syntax. Other engines share most of this but differ in the details.
Character classes
- .
- Any character except a line break
- \d
- A digit, 0 to 9
- \D
- Not a digit
- \w
- Letter, digit or underscore
- \W
- Not a word character
- \s
- Any whitespace
- \S
- Not whitespace
- [abc]
- Any one of a, b or c
- [^abc]
- Any character except those
- [a-z]
- Any character in the range
Quantifiers
- *
- Zero or more
- +
- One or more
- ?
- Zero or one — optional
- {n}
- Exactly n times
- {n,}
- n or more times
- {n,m}
- Between n and m times
- *?
- Lazy — as few as possible
- +?
- Lazy one or more
Anchors
- ^
- Start of string, or line with m
- $
- End of string, or line with m
- \b
- Word boundary
- \B
- Not a word boundary
Groups
- (...)
- Capture group, numbered left to right
- (?:...)
- Group without capturing
- (?<n>...)
- Named capture group
- \1
- Backreference to group 1
- \k<n>
- Backreference to a named group
- |
- Alternation — either side
Lookaround
- (?=...)
- Positive lookahead
- (?!...)
- Negative lookahead
- (?<=...)
- Positive lookbehind
- (?<!...)
- Negative lookbehind
Lookbehind needs a reasonably current browser. JavaScript allows variable-length lookbehind, which several other engines do not.
Escapes
- \.
- A literal dot
- \\
- A literal backslash
- \n
- Newline
- \t
- Tab
- A specific code unit
- \u{1F600}
- A code point, needs u or v
- \p{L}
- Unicode property, needs u or v
Regex engine
This tester uses JavaScript RegExp syntax, evaluated by the browser you are reading this in. Other regex engines may behave differently. Here is what your browser reports:
Share pattern copies a link containing only the pattern, flags and replacement. Share with test text adds your test text to that link — use it only when the text is not sensitive. Your pattern and flags are remembered in this browser; the test text never is.
What is a regex tester?
A regex tester is a workbench for a single, specific problem: you have written a pattern, and you need to know what it actually matches before it goes anywhere near production code. Reading a regular expression and predicting its behaviour is genuinely hard, even for people who write them daily. Running it against real text and seeing every match highlighted removes the guesswork.
The useful parts are not the matching itself but everything around it. Which matches were found, and where. What each capture group actually captured, including the ones that matched an empty string and the ones that did not participate at all. Whether the pattern you think is anchored is really anchored. What the text looks like after a replacement. A tester that shows only a green tick or a red cross tells you almost nothing; one that shows positions, groups and a live preview lets you debug.
The tool above updates as you type. There is no run button to press for ordinary testing: change the pattern, a flag or the text, and the highlighting, match count and group breakdown all follow.
What is a regular expression?
A regular expression is a compact description of a text pattern. Instead of searching for one fixed string, you describe a shape — three digits, then a hyphen, then four digits — and the engine finds every piece of text with that shape.
Most characters in a pattern stand for themselves. The word cat is a valid
regular expression that matches the letters c, a, t in order. Power comes from the
metacharacters: . for any character, + for one or more,
[a-z] for a range, | for either side. Combine those and you can
describe most of the textual shapes that turn up in ordinary programming work.
The name comes from formal language theory, where a regular language is one a finite automaton can recognise. Practical regex engines long ago outgrew that definition — backreferences and lookaround are not regular in the theoretical sense — but the name stuck. It matters in one practical way: because these features go beyond the theory, they also go beyond the algorithms that guarantee fast matching, which is why the section on backtracking below exists at all.
How to test a regex
A reliable routine looks like this.
- Start with real text. Paste actual data, not an idealised sample. Real data has trailing spaces, inconsistent casing and the one weird row that breaks everything.
- Include negatives. Add lines that should not match. A pattern that matches everything you feed it usually matches far more than you intended.
- Build up in stages. Get
\d+working before you wrap it in groups and anchors. Each addition should change the match count in a way you predicted. - Check the groups, not just the match. The full match can look right while group 2 quietly captured an empty string.
- Watch the count. If you expected 12 matches and got 240, something is matching the empty string at every position.
- Test the edges. The first line, the last line, an empty line, a line with only whitespace.
The match ribbon under the editor helps with the fourth and fifth points: it shows where matches fall across the whole text at a glance, so a cluster at the start or an unexpectedly even spread is obvious immediately.
Regex syntax basics
Twelve characters have special meaning outside a character class:
. * + ? ^ $ | \ ( ) [ {. To match one of them literally, put a backslash in
front: \. matches a real dot, and \$ matches a dollar sign.
This is the single most common source of patterns that almost work. An unescaped
. in example.com matches any character, so it also matches
exampleXcom. Writing example\.com fixes it.
Inside a character class the rules relax: [.+*] matches a literal dot, plus or
asterisk with no escaping needed, because those characters have no special meaning there.
The characters that still need care inside a class are ], \,
^ when it is first, and - when it sits between two others.
Regex character classes
A character class matches exactly one character from a set. [aeiou] matches a
single vowel. [0-9a-fA-F] matches one hexadecimal digit. Putting
^ immediately after the opening bracket negates the whole set, so
[^0-9] matches any single character that is not a digit.
The shorthand classes cover the common cases: \d for digits,
\w for word characters, \s for whitespace, and their negated
uppercase forms \D, \W and \S.
One JavaScript-specific detail worth internalising: \d is always exactly
[0-9] and \w is always exactly [A-Za-z0-9_], even
with the u flag on. They never expand to cover Devanagari digits or accented
letters. \s is the exception — it does include a range of Unicode whitespace.
If you need letters from any script, use \p{L} with the u flag,
which the hashtag example above demonstrates.
Regex quantifiers
Quantifiers say how many times the preceding token may repeat. * means zero
or more, + means one or more, and ? makes something optional.
For precise counts, {3} means exactly three, {2,} means two or
more, and {2,5} means between two and five.
A quantifier binds to the single token immediately before it, which is why
abc+ means "ab followed by one or more c", not "one or more of abc". For the
latter you need a group: (?:abc)+. The explanation panel above makes this
explicit — it deliberately splits abc+ into a literal ab and a
separate c so the quantifier's real target is visible.
By default quantifiers are greedy: they consume as much as they can and
then give characters back only if the rest of the pattern fails. Adding ?
makes them lazy, consuming as little as possible. Against
<b>bold</b>, the pattern <.+> matches the whole
string, while <.+?> matches just <b>. Paste both into
the tester and watch the highlight change — it is the clearest way to internalise the
difference.
Regex anchors
Anchors match a position rather than a character. They consume nothing, which is why they show as a thin caret in the highlighted preview instead of a coloured block.
^ matches the start of the string and $ the end. With the
m flag they instead match at the start and end of every line, which is what
makes per-line validation possible — the username example above relies on exactly this.
\b matches a word boundary: the position between a word character and a
non-word character. It is what makes \bcat\b match the word "cat" without
also matching the "cat" inside "concatenate". Because a boundary is a position and not a
character, \b never appears in the matched text.
Regex groups
Parentheses group part of a pattern so a quantifier or alternation applies to the whole
thing. (ab)+ matches "ab", "abab", "ababab". Without the parentheses,
ab+ would mean something quite different.
A plain (...) group also captures. When you only want the grouping and not
the capture, use (?:...). It keeps your group numbers meaningful and saves the
engine a little work. The IPv4 example above uses (?:...) throughout for
exactly this reason: the octet pattern needs grouping, but there is nothing worth
capturing.
Capture groups
A capture group remembers what it matched so you can pull it out afterwards. Groups are numbered from one, in the order their opening parentheses appear — which matters when groups nest, because the outer group always gets the lower number.
Against 2026-08-09, the pattern
(\d{4})-(\d{2})-(\d{2}) produces group 1 2026, group 2
08 and group 3 09.
Two group states are easy to confuse and the panel above distinguishes them explicitly. A
group that matched but captured nothing shows (empty string) — this happens with
patterns like (x*). A group inside an alternative branch that was never taken
shows did not participate, and its value in code is undefined, not
an empty string. Treating those two cases as the same thing is a reliable source of bugs.
Named capture groups
Counting parentheses stops being fun quickly. Named groups, written
(?<name>...), let you label a group instead:
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
In code the results arrive on match.groups.year rather than
match[1], which survives someone later inserting a group in the middle. You
can reference a named group elsewhere in the pattern with \k<name>, and
in a replacement with $<name>. Names must be unique within a pattern —
reusing one is a syntax error, which the validity line above will tell you about.
Named groups are still numbered as well, so (?<year>\d{4}) is both
"year" and group 1. The tester shows both, so you can see the mapping directly.
Regex flags
Flags change how the whole pattern is applied.
- g — global. Keep searching after the first match. Without it you get at most one match, and replace changes only the first occurrence.
- i — ignore case. Letters match in either case.
- m — multiline.
^and$match at line boundaries rather than only at the string boundaries. - s — dotAll. The dot also matches newlines. Useful when matching across lines; risky when combined with
.*. - u — unicode. Treats the pattern as code points, enables
\u{...}and\p{...}, and makes some sloppy escapes an error instead of a silent oddity. - v — unicode sets. A newer, stricter Unicode mode adding set operations inside classes. It cannot be combined with
u; selecting one here turns the other off. - y — sticky. The match must begin exactly where the previous one ended. Useful for tokenisers, surprising everywhere else.
- d — indices. Records the start and end offsets of every capture group. Turn it on and each group in the details panel gains a position range.
The g flag is on by default here because most people arrive wanting to see
every match. Nothing else is ever added for you — if a pattern behaves differently from
your code, compare the flag row above with the flags in your source.
Regex lookahead
Lookahead checks what comes next without consuming it. (?=...) requires a
match at the current position; (?!...) requires that there is none.
Because it consumes nothing, you can stack several lookaheads at one position, which is how the password example works:
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$
Each lookahead scans ahead from the start for one required character type, then returns
to position zero. Only after all four succeed does .{8,} do the actual
matching. This is the standard way to express "must contain all of these, in any order",
a requirement that is otherwise awkward to write.
A negative lookahead is equally useful for exclusions: \b(?!test)\w+\b
matches words that do not start with "test".
Regex lookbehind
Lookbehind does the same thing backwards. (?<=...) requires the text
before the current position to match, and (?<!...) requires that it does
not. To grab an amount after a currency symbol without including the symbol in the match,
(?<=\$)\d+(?:\.\d{2})? does the job.
Lookbehind arrived in JavaScript with ES2018 and works in all current browsers. One
genuine advantage the JavaScript engine has here: it supports
variable-length lookbehind, so (?<=cat|elephant)\s\w+ is
legal. Several other engines, including PCRE and Java, require the lookbehind to be a
fixed length and will reject that pattern. It is a good example of why a pattern proven
in this tester still needs checking against the engine you will actually deploy on.
Regex alternation
The pipe | means "either side". Its precedence is very low, so it splits the
largest surrounding scope: ^cat|dog$ means "starts with cat, or ends with
dog", which is almost never the intent. Group it to control the scope:
^(cat|dog)$.
Alternation is ordered. The engine tries branches left to right and stops at the first
that lets the overall match succeed — it does not look for the longest one. Matching
cat|catalog against "catalog" yields "cat", because the first branch
succeeded. Put longer, more specific alternatives first.
Regex replace
Replace mode reuses the pattern you already have and applies it to the same test text, so
there is no second pattern to keep in sync. The replacement follows JavaScript's own
rules, which means what you see here is what
String.prototype.replace will produce in your code:
$&— the entire match$1,$2— numbered capture groups$<name>— a named capture group$$— a literal dollar sign$`— everything before the match$'— everything after the match
Reformatting a date is a one-liner: with pattern
(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2}) and replacement
$<d>/$<m>/$<y>, every ISO date becomes day/month/year.
Whether every match is replaced depends entirely on the g flag, exactly as it
does in JavaScript. With g off, only the first match changes, and the panel
says so rather than quietly adding the flag for you. The statistics line always reports
both the match count and the replacement count, and it tells you when the output is
identical to the input — a replacement that changes nothing is a real result, not a
failure to report.
Match highlighting
Highlighted matches use a honey background; the currently selected match switches to solid violet with a ring around it, so it stays identifiable without relying on colour alone. Stepping through with the Prev and Next buttons scrolls each one into view and updates the details panel beneath.
Zero-width matches get special treatment. A pattern like \b or
^ matches a position, so there is no text to shade. Rather than rendering
nothing at all, the preview draws a thin caret at that position. Without it, a pattern
reporting "14 matches" against text with no visible highlighting would look broken.
Everything in the preview is rendered as text, never as markup. You can paste
<script>alert(1)</script> into the test box and it will appear as
those exact characters — the highlighter builds text nodes and <mark>
elements directly rather than assembling an HTML string, so there is no path by which
pasted content can execute.
JavaScript regex syntax
This tester runs your browser's own RegExp implementation, so the results are
exactly what JavaScript produces. In code you will meet regular expressions through a few
methods, and the differences between them cause a surprising amount of confusion.
test() returns a boolean. match() returns match data, or an
array of plain strings when the regex is global. matchAll() returns an
iterator of full match objects including groups, and is usually what you actually want.
exec() returns one match at a time and is designed to be called in a loop.
replace() and replaceAll() perform substitution.
The most notorious JavaScript-specific trap involves lastIndex. A regex
object with the g or y flag remembers where its last match
ended, and that state lives on the object itself. So a regex stored in a module-level
constant and reused across calls to test() will appear to alternate between
true and false on identical input:
const re = /\d+/g;
re.test('abc 123'); // true, lastIndex is now 7
re.test('abc 123'); // false, it resumed from index 7
re.test('abc 123'); // true, lastIndex reset to 0 after failing
The fixes are to create the regex where you use it, drop the g flag when you
only need a yes-or-no answer, reset lastIndex yourself, or use
matchAll(). The tester avoids this class of bug internally by compiling a
fresh regex for every evaluation.
JavaScript regex vs other engines
Regex syntax is broadly similar across languages and subtly different everywhere it matters. A pattern that works here may need adjusting elsewhere.
- Named groups. JavaScript, .NET and modern PCRE use
(?<name>...). Python accepts(?P<name>...), and refers to them as(?P=name)inside the pattern. - Atomic groups and possessive quantifiers. PCRE, Java and Ruby offer
(?>...)anda++to prevent backtracking. JavaScript has neither, so ReDoS has to be avoided by pattern design instead. - Inline flags. Many engines allow
(?i)inside the pattern. JavaScript does not — flags go only on the literal. - String anchors.
\A,\zand\Zexist in Python, Java and PCRE but not in JavaScript, where you use^and$without themflag. - Shorthand scope. Python's
\dmatches Unicode digits by default in Python 3; JavaScript's never does. - Lookbehind length. JavaScript and .NET allow variable-length lookbehind. PCRE and Java require a fixed length.
- Linear-time engines. Go's
regexppackage and RE2 guarantee linear-time matching, and pay for it by not supporting backreferences or lookaround at all.
Common regex mistakes
- An unescaped dot.
.matches any character. Write\.when you mean a literal dot. - Greedy
.*swallowing too much. Use a lazy.*?, or better, a negated class such as[^<]*that cannot cross the boundary in the first place. - Forgetting the
gflag. Then wondering why only one match, or one replacement, happened. - Expecting
^and$to work per line. They do not, until you add themflag. - Reusing a global regex object. The
lastIndexproblem described above. - Patterns that can match nothing.
\d*succeeds while consuming zero characters, so withgit matches at every position. Use+when you require at least one. - Parsing HTML. Nesting is unbounded, and regular expressions cannot track it. Use
DOMParserfor anything structural. - Over-engineering email validation. The full grammar is enormous, and the only real proof an address works is sending mail to it. Match loosely, then verify.
- Confusing
[]with().[cat]matches one character from c, a, t.(cat)matches the word. - Assuming index equals character count. JavaScript indexes UTF-16 code units, so an emoji counts as two.
Regex performance and backtracking
JavaScript's engine is a backtracking engine. When a pattern can match in more than one way, it tries one path and, on failure, returns to the last decision point and tries the next. Usually this is fast. Occasionally it is catastrophic.
The trouble comes from nested or overlapping quantifiers. Consider
(a+)+$ against a string of twenty a characters followed by a
single b. The b guarantees failure, but before conceding, the
engine tries every way of splitting those twenty characters between the inner and outer
quantifiers. The work doubles with each additional character: thirty is slow, forty may
take minutes, fifty is effectively forever.
On a server this is a denial-of-service vector — a single crafted input can pin a CPU core. It has caused real, widely reported outages, and it is the reason input validation patterns deserve a second look before deployment.
To keep the page usable while you experiment, matching here runs in a Web Worker. A
running regular expression cannot be interrupted from inside, so the only reliable escape
is to terminate the worker outright — which is exactly what happens if a pattern exceeds
its time budget. You get a message suggesting you simplify the pattern, and the interface
stays responsive throughout. Try (a+)+$ against a long run of
as ending in b if you want to see the protection work.
Writing patterns that avoid the problem comes down to a few habits: prefer a negated
character class over .* where you can, avoid quantifying a group whose
contents are themselves quantified, anchor patterns so failure is detected early, and be
suspicious of alternations whose branches can match the same text. If you genuinely need
guaranteed linear time, that requires a different engine such as RE2 — no amount of care
makes a backtracking engine immune.
Frequently asked questions
What is a regex tester?
A regex tester is a tool that runs a regular expression against sample text and shows you exactly what it matches, so you can debug the pattern before putting it in code.
What is a regular expression?
A regular expression is a compact description of a text pattern. Instead of searching for one fixed string, it describes a shape such as a digit followed by three letters, and finds every piece of text with that shape.
How do I test a regex?
Enter the pattern, pick your flags, paste representative text including examples that should not match, then step through the matches and check each capture group.
What regex syntax does this tool use?
JavaScript RegExp syntax, executed by your own browser. Other engines such as PCRE, Python, Java, Go and .NET differ in places, so a pattern that works here may need adjusting elsewhere.
Can I test multiple matches at once?
Yes. Turn on the g flag, which is enabled by default, and every match in the text is found, counted and highlighted.
What does the g flag mean?
The global flag makes the engine keep searching after the first match instead of stopping. Without it you get at most one match, and replace only changes the first occurrence.
What does the i flag mean?
The ignore-case flag makes letters match regardless of case, so cat also matches Cat and CAT.
What does the m flag mean?
The multiline flag changes ^ and $ so they match at the start and end of each line rather than only at the start and end of the whole string.
What does the s flag mean?
The dotAll flag lets the dot match line breaks too. Without it, a dot matches any character except a newline.
What are capture groups?
Parentheses around part of a pattern capture whatever that part matched, so you can pull the pieces out separately. They are numbered from left to right by their opening parenthesis.
What are named capture groups?
Written as (?<name>...), they give a group a label so you can refer to it by name instead of counting parentheses. Named groups are supported in modern browsers.
Can I replace matches?
Yes. Open replace mode and enter a replacement string. JavaScript replacement tokens work exactly as they do in code: $& for the whole match, $1 for a numbered group and $<name> for a named group.
Can I see match positions?
Yes. Each match shows its start index, end index, length, and the line and column where it begins. Indexes are JavaScript string indexes, counted in UTF-16 code units.
Can I test multiline text?
Yes. Newlines, tabs and repeated spaces are preserved exactly in both the editor and the highlighted preview. Turn on the m flag if you want ^ and $ to work per line.
Can I copy my regex?
Yes. You can copy the raw pattern, the complete /pattern/flags literal, the test text, an individual match, or the full replacement result.
Can I use this on mobile?
Yes. The layout stacks into a single column on small screens with full-width editors and touch-friendly controls.
Is the tool free?
Yes, completely free with no account and no usage limits.
Is my test text uploaded anywhere?
No. Your pattern and test text are evaluated by your own browser. There is no backend for this tool and nothing you type is sent to a server.
Is it safe to paste HTML or script tags as test text?
Yes. Test text is rendered as text nodes, never as markup, so a pasted script tag appears as literal characters and cannot execute.
Can a regular expression be dangerous?
A badly shaped pattern can take exponential time on certain inputs, which is a real denial-of-service risk on a server. This tester runs matching in a Web Worker and cancels it after a short budget, so the page stays responsive.
What is catastrophic backtracking?
When a pattern has nested or overlapping quantifiers, the engine can try an enormous number of ways to split the input before admitting failure. A pattern like (a+)+$ against a long run of a characters followed by one b can effectively hang.
Why is my regex invalid?
Usually an unclosed group or character class, a quantifier with nothing to repeat, a duplicate group name, or a stray backslash. The status line under the pattern names the specific problem.
Why does my regex behave differently in another language?
Engines differ. Python uses (?P<name>...) for named groups, PCRE allows atomic groups and possessive quantifiers that JavaScript lacks, and shorthand classes like \d may cover non-ASCII digits in some engines but never in JavaScript.
What is the difference between regex and glob patterns?
Glob patterns are the simple wildcards shells use for filenames, where * means any run of characters. In a regular expression * means zero or more of the previous token, so the two languages read very differently even where they share symbols.
Why does my pattern match an empty string everywhere?
Patterns built only from optional parts, such as a* or \d*, can succeed while consuming nothing, so they match at every position. Requiring at least one character with + usually fixes it.
Should I use a regular expression to parse HTML?
Not for anything structural. HTML nests arbitrarily and regular expressions cannot track nesting reliably. A regex is fine for a quick scan of simple, known markup, but use a real parser such as DOMParser for anything you depend on.
Related developer tools
Other browser-based utilities from ToolAdda.
Debug your next pattern here
Live highlighting, capture groups, match positions and replace mode — free, instant, and entirely in your browser.
Back to the tester