Hacks VitaeAll tools
freecompressconvert
Precision · Instant · Private

Regex Tester

Test regular expressions against your text with live match highlighting and a match count. Supports global, case-insensitive, and multiline flags.

SYSTEM ● ONLINE · LOCAL COMPUTE · ZERO UPLOAD
UNIT // REGEX.TESTLIVE
0
Matches
—
Pattern
Quick Answer

How do you test a regular expression?

// Answer

Enter your regex pattern and flags, then paste sample text. The tester highlights every match live and counts them. Flags change behavior: g finds all matches, i is case-insensitive, and m makes ^ and $ match line starts and ends.

Why use this tool

Debug patterns safely

Regex is powerful but easy to get wrong. Testing against real examples with live highlighting helps you see exactly what matches before you ship it. Everything runs locally using the browser’s own regex engine, the one your JavaScript uses in this browser.

FAQ

Frequently asked questions

JavaScript (ECMAScript) regular expressions, the same standard that browsers and Node.js follow. Each browser has its own engine, so a very new feature can reach one before another.
g = global (all matches), i = case-insensitive, m = multiline, s = dotall, u = unicode, v = Unicode sets (a stricter form of u), y = sticky, d = start and end indices for each match.
The pattern has a syntax error such as an unbalanced bracket or parenthesis. The tool shows the error.
Yes, matching happens entirely in your browser.
More tools

Related tools

Worth knowing

What this tester shows you, and what it leaves out

// Answer

The highlight pane shows whole matches only. Capture groups are not broken out, and zero-length matches are skipped rather than counted. Highlighting also always behaves as if the g flag were set, so you see every match in the text even when you left g off.

Those three behaviours explain almost every surprise on this page. With (\d{4})-(\d{2}) you see the full date highlighted but not the year and month separately — the tool reports what matched, not what was captured, and the copy button gives you the same whole matches. With a pattern that can match nothing, such as \d*, the counter looks far too low, because a zero-length match has nothing to highlight and is passed over. And because the highlighter adds g for you, the readout counts every occurrence, which is not what String.prototype.match returns for a non-global pattern.

Patterns containing <, > or & work as written: the pattern runs over your raw text, and only the highlighted copy is escaped for display. A pattern looking for <div> finds it.

Worked example

The default pattern is deliberately wrong

The page loads with \b\w+@\w+\.\w+\b and two example addresses, and it matches both of them. Now replace the sample text with [email protected] and watch what happens: the highlight lands on [email protected] and nothing else.

Nothing is broken. \w means letters, digits and underscore — it does not include a dot. So \w+ cannot cross first.last, and cannot reach past example to take in .co.uk. The pattern was never an email validator; it is a sketch that fits simple addresses, and when a real one appears it quietly matches the wrong substring rather than failing loudly. Hence the habit worth taking from this tool: always test a pattern against input you expect it to reject. A partial match is more dangerous than no match, because it looks like success.

Here is a second experiment that takes ten seconds and prevents a genuinely confusing bug. Set the pattern to \A and the flags to g. The readout says Valid, and any capital A in the text is highlighted. Now add u to the flags. The same pattern is suddenly Invalid. \A is a start-of-string anchor in Python, Perl and Ruby, and it does not exist in JavaScript at all — without the u flag JavaScript treats an unknown escape as the literal character, so you get silent nonsense instead of an error. Patterns copied from another language fail exactly this quietly. Named groups carry the same trap — JavaScript writes (?<year>\d{4}), Python writes (?P<year>\d{4}) — and JavaScript has no atomic groups or possessive quantifiers at all.

Security

A regex can be a denial-of-service bug

JavaScript uses a backtracking regex engine, and backtracking can explode. The classic shape is a repeated group inside another repetition, like (a+)+$. Run that against a long run of a characters ending in a b and the engine has to try an enormous number of ways to divide the input before it can conclude there is no match. The work grows exponentially with the length of the input. This is called catastrophic backtracking, or ReDoS.

Do not paste that pattern here with a long input unless you are willing to close the tab. There is no worker and no timeout in this page; the matching runs on the main thread, which is exactly why a pathological pattern freezes the interface. That is a property of the engine, not a defect specific to this tool, and seeing it happen locally is much cheaper than seeing it happen in production.

Which leads to the rule that matters: never run a regex supplied by a user against input of unbounded length without a guard. A search box that accepts a raw pattern, or a validation regex loaded from a config file someone else can edit, is a way for one request to consume a CPU core indefinitely. Bound the input length, run matching with a timeout in a process you can kill, or use an engine with linear-time guarantees such as RE2 or Rust's regex crate. Reviewing your own patterns for nested quantifiers before they ship costs nothing.

The mistake

Reaching for regex when a parser exists

Regex matches flat patterns in text. It cannot count nesting, which rules out every format that can contain itself. If the thing you are matching has a real grammar, use something that knows the grammar.

What you are trying to doRegex?Better approach
Pull a value out of JSONNoParse it. The JSON formatter will also show you where it is malformed.
Extract text from HTMLNoA DOM parser. Nested tags defeat any pattern you write.
Split a CSV rowNoA CSV reader — quoted commas and embedded newlines are legal. See CSV to JSON.
Validate an email addressBarelyCheck for one @ with something either side, then send a confirmation message. Delivery is the only real proof.
Find and change a literal stringOverkillFind and replace does it without a pattern to get wrong.
Match a date, an ID, a log line formatYesThis is what regex is genuinely good at.
Why local matters

The sample text is the problem

Patterns are not developed against tidy examples. They are developed against the awkward real thing — a slice of a production log, a chunk of a customer export, the API response that broke the importer. That text carries email addresses, tokens, order numbers and names, and pasting it into a remote regex service uploads all of it to somewhere with no obligation to you. The pattern is rarely sensitive. The text you are testing it on almost always is.

Nothing leaves this page. The script reads the three inputs, builds a RegExp with the browser's own engine, and writes highlighted HTML into the pane below; there is no fetch call and no XMLHttpRequest in it. You can prove that in five seconds — open your network panel, type into the pattern field, and watch nothing happen. (The page itself reports a view when it loads; typing sends nothing.) Or switch the network off and keep working.

Using the browser's own engine has a second benefit: the behaviour you observe here follows the same ECMAScript rules as Node and other modern browsers, rather than a server-side flavour such as Python's or PCRE that differs in the details. Text comparison, keyword density and the rest of the set stay local in the same way; the full list is on the tool index.

Last reviewed 2 October 2026

4 more

More developer tools

All 18 →

Everything here runs in your browser. Browse all the tools, or start from the home page.

Ask an assistant about this page

Opens in a new tab with this page and the question already filled in.