What a regex tester does, and why reading a pattern fails
A regular expression is a small language for describing the shape of text. This page compiles what you type with new RegExp(pattern, flags) and runs it against your test string using the JavaScript engine already in your browser, so the syntax accepted here is ECMAScript, the same dialect you get in Node.js and the browser console. It is not PCRE, not Python re and not POSIX grep.
The reason to run a pattern rather than read it is that regex fails quietly. A pattern that compiles will happily match the wrong span, capture one character too many, or find three matches where you expected thirty, and nothing in the source code says so. Reading a.*b does not tell you that the greedy .* ran all the way to the last b in the line. Highlighting the matches in place, with the index and the captured groups listed underneath, turns that guesswork into something you can see.
Three details of the output are worth knowing before you trust it. The tester honors your flags exactly, so without g it shows only the first match, which is the right behavior when you are checking an anchored validation pattern. Overlapping matches are not possible, because the engine resumes scanning at the end of the previous match. And a group that took part in a match but captured nothing is shown as an empty string rather than being omitted, which is how you catch an optional group that never actually fired.
Which flag and which tab for which job
gglobal: find every match instead of stopping at the first. Turn it off when the pattern is anchored with^and$for form validation, and on when you are pulling every address or error code out of a log.iignoreCase: upper and lower case match each other. Useful for hex colors, UUIDs and HTML tags, where both cases occur in the wild.mmultiline:^and$match at every line break rather than only at the ends of the whole string. This is the flag people forget when a pattern that works on one line finds nothing in a pasted log.sdotAll:.also matches a newline. Needed when a match has to span lines, for example the body of a multi line comment.uunicode: treat the pattern as a sequence of code points and enable\p{...}property escapes. Required before\p{L}or\u{1F341}will compile, and the safer default whenever accented letters, CJK, or emoji appear in your data.ysticky: match only at the currentlastIndexposition. Mostly used when hand writing a tokenizer, rarely useful for ad hoc testing.- The Match tab answers whether and where a pattern hits. The Replace tab answers what the text will look like afterwards, which is the question that matters before you run a find and replace across a repository.
How to build a pattern from a sample
- Paste real text into the test field first, before you write anything. Patterns built against imagined input tend to break on the first line of an actual file.
- Start from the loosest thing that could work. For an error code, begin with
ERRORand watch it light up, then grow it into\bERROR\b.*once you can see what it is picking up. - Turn on
gso you can see every hit at once, andmif your text has several lines and your pattern uses^or$. - Tighten one piece at a time. Swap
.for a specific class such as[^\s]or\d, and replace.*with.*?if the highlight is running past where it should stop. - Wrap the part you actually want in parentheses so it becomes a capture group, then expand the details block and check that the first group holds exactly the substring you want, not the whole line.
- Try to break it. Paste a line that should not match and confirm the count drops to zero. A validation pattern that has never been shown a bad input has not been tested.
How to do a find and replace with capture groups
- Get the match right in the Match tab first. Replacing with a pattern you have not seen highlighted is how a bulk edit goes wrong.
- Switch to Replace and write the replacement. Numbered groups are
$1,$2and so on, a named group is$<name>, the whole match is$&and a literal dollar sign is$$. - Keep
gon for a bulk edit. Without it only the first occurrence changes, which is easy to miss when the preview is long. - Read the preview from the bottom as well as the top. Mistakes tend to hide in the last line, where an optional group did not fire and
$2quietly became an empty string. - Copy the pattern and the replacement into your editor and run the same edit there. Reformatting a date is a good first exercise: match
(\d{4})-(\d{2})-(\d{2})and replace with$3/$2/$1.
Named groups make a long replacement readable. Matching (?<year>\d{4})-(?<month>\d{2}) and replacing with $<month>/$<year> still says what it means six months later, which $2/$1 does not.
Getting the pattern into an editor, a shell or your code
The pattern in the box is the bare pattern, with no surrounding slashes. Where it goes next decides how much of it you have to escape:
- A JavaScript literal: wrap it in slashes and put the flags after, as
/\d+/gi. Nothing else changes. - A JavaScript string: every backslash has to be doubled, because the string literal eats one first. This is the single most common reason a pattern that worked in a tester stops working in code.
- VS Code and most editors: open Find, click the
.*icon to enable regex, and paste the pattern without slashes. Case sensitivity and whole word are separate buttons rather than flags, and the replacement field uses the same$1syntax as here. - The shell: wrap the pattern in single quotes so the shell does not expand it, as in
grep -E 'pattern' file.log. Note that command line tools use a different dialect, covered in the next section. - Phones: press and hold in the field and choose Paste on iPhone, iPad and Android. The test area is a plain text field, so keyboard autocorrect can capitalize the first letter of a pattern; check the first character after pasting from a note.
- Sharing with a colleague: send the pattern, the flags and one line of sample input together. A pattern on its own is not reviewable, and the sample is what makes a bug obvious.
The same pattern in other languages and tools
Most of regex is portable. The parts that are not tend to be named groups, what the shorthand classes mean under Unicode, and whether the engine backtracks at all. In JavaScript, the dialect this page runs, the literal and the constructor differ only in escaping:
const re = /(?<user>[\w.+-]+)@(?<host>[\w.-]+)/gi
const same = new RegExp('(?<user>[\\w.+-]+)@(?<host>[\\w.-]+)', 'gi')
text.match(re) // an array of every match, because of g
re.exec(text).groups.user // a named capture from the next matchPython spells named groups differently and defaults to Unicode aware shorthand classes on str, so \d there matches digits in other scripts unless you ask for ASCII. It also has no \p{...} property escapes in the standard library:
import re
m = re.search(r'(?P<user>[\w.+-]+)@(?P<host>[\w.-]+)', text, re.IGNORECASE)
m.group('user')
re.findall(r'\d+', text, re.ASCII) # ASCII digits onlyJava uses the same (?<name>...) syntax as JavaScript, but its \d and \w are ASCII only until you compile with UNICODE_CHARACTER_CLASS, and every backslash has to be doubled inside the Java string literal. Go and Rust use the RE2 engine, which guarantees linear time by refusing to support backreferences and lookaround at all; a pattern with (?<=...) will not compile there, and the fix is to capture the context in a group instead.
On the command line the default is older and narrower than anything above. Plain grep and sed take POSIX basic expressions, where + and ? are literal characters unless escaped; adding -E switches both to extended expressions, which is the form most people mean. GNU grep also has -P for the PCRE dialect, which is the closest to what you tested here, but it is not present in the BSD grep that ships with macOS:
grep -E '[0-9]{3}-[0-9]{4}' contacts.txt # extended, portable
grep -P '\d{3}-\d{4}' contacts.txt # PCRE, GNU grep only
sed -E 's/([0-9]{4})-([0-9]{2})/\2\/\1/' dates.txtNote the replacement syntax changes too: sed and most POSIX tools use \1 where JavaScript uses $1. Databases differ again. Postgres offers ~ and regexp_match with its own POSIX based flavor, MySQL 8 uses ICU expressions through REGEXP_LIKE, and SQLite understands the REGEXP operator but ships no implementation, so it raises an error until the application registers one. Treat a pattern that has to run in the database as a separate thing to test, not as a copy of this one.
Limits, privacy and honest caveats
What leaves your browser: nothing. The pattern and the test string are compiled and matched locally by your own browser and are never uploaded, logged or sent anywhere, so a real log excerpt or a customer record can go straight in without being scrubbed first. The tool keeps your last five pattern and text pairs in the localStorage of this browser so the history panel can restore them, and the Clear button there removes them.
A nested quantifier can hang your tab. The engine here backtracks, so a pattern such as (a+)+b against a long non matching string can try an exponential number of combinations. That is the same weakness behind the ReDoS denial of service attack when such a pattern reaches a server. Matching runs on the main thread of this page, so the tab will freeze rather than report an error. The match list is capped at 10,000 results as a safety valve, which means a very loose pattern on a very long text shows the first ten thousand matches only.
Regex is not a parser. HTML, JSON, CSV with quoted commas and source code are all nested or context sensitive, and no regular expression handles nesting correctly. Use a pattern to find candidates or to make a one off edit you will read afterwards, and a real parser when correctness matters. The same caution applies to email: RFC 5322 is far more permissive than any practical pattern, so validate loosely and confirm by sending a message.
Browser differences are small but real. Lookbehind, written (?<=...) and (?<!...), reached Safari in version 16.4 in 2023 after years in Chrome and Firefox, so a pattern that compiles on your laptop may report a syntax error on an older iPhone. If you see an error you cannot explain, that is the first thing to test.
Reading the highlight. Matches are marked with a yellow background, which is color alone. The details list under the result gives the same information as text, with the exact substring and its index for every match, so nothing in the output depends on being able to see the highlight.
Frequently asked questions
Which regex flavor does this tester use?
JavaScript, also called ECMAScript. The pattern you type is handed to new RegExp(pattern, flags) and run by the engine already in your browser, so the result is the same one you would get from Node.js or the browser console. It is not PCRE, not Python re and not POSIX, so a pattern that only works in one of those will behave differently here.
Why does my pattern work here but fail in Python or Java?
Roughly nine tenths of regex syntax is shared and the last tenth is where the dialects split. The most common trip points are named groups, which JavaScript writes as (?<name>...) while Python writes (?P<name>...), and character classes, where JavaScript \d always means the ten ASCII digits while Python 3 matches Unicode digits on a str unless you pass re.ASCII. Go and Rust use the RE2 engine, which has no backreferences and no lookaround at all.
What is the difference between greedy and lazy quantifiers?
Quantifiers are greedy by default, meaning they take as much as they can and then give characters back only if the rest of the pattern fails. Against the text axxxbxxxb the pattern a.*b stops at the last b. Add a question mark to make the quantifier lazy, so a.*?b stops at the first b. Paste both into the tester and watch the highlight move; that is usually faster than reasoning it out.
What does the g flag actually change?
Without g the engine reports only the first match, which is what you want when the pattern is anchored with ^ and $ for validation. With g the tester loops and shows every match. In your own code the g flag also makes the RegExp object stateful: it keeps a lastIndex property between calls, so reusing one global regex across test() calls returns alternating true and false. Create the regex fresh, or reset lastIndex to 0.
How do I read the groups and index in the details list?
Expand the details block under the highlight. For each match, index is the zero-based character offset where the match starts in your test string, groups lists the numbered captures in order, and named shows any (?<name>...) captures as an object. A group that took part in the match but captured nothing appears as an empty string, which is how you spot an optional group that never fired.
How do I use capture groups in the replacement?
Switch to the Replace tab and refer to a numbered group as $1, $2 and so on, or to a named group as $<name>. A literal dollar sign is written $$, and $& inserts the whole match. Without the g flag only the first occurrence is replaced, which catches people out when they copy the replacement into code and wonder why one line changed.
Why does my email pattern miss some valid addresses?
Because the grammar in RFC 5322 allows quoted local parts, comments and other shapes almost nobody writes, so a truly complete email regex is famously enormous and still does not tell you whether the mailbox exists. The practical approach is a loose pattern that rejects obvious typos, then a confirmation email to prove the address is real. Be especially careful not to reject plus addressing or long new top level domains.
What is catastrophic backtracking, and how do I avoid it?
When a pattern nests one quantifier inside another, such as (a+)+b, a backtracking engine can try an exponential number of ways to split the same text before giving up on a near miss. A few dozen characters can hang a thread, which is the basis of the ReDoS denial of service attack. Avoid two quantifiers covering the same characters, prefer a specific character class over a dot, and anchor the pattern where you can.
Do lookbehind assertions and \p{L} work in this tester?
Both are part of modern ECMAScript, so they work wherever your browser supports them. Lookbehind, written (?<=...) and (?<!...), has been in Chrome and Firefox for years and arrived in Safari 16.4 in 2023, so an old iPhone may still report a syntax error. Unicode property escapes such as \p{L} for any letter require the u or v flag; turn on u before you use them.
Does my test text leave my browser?
No. The pattern and the test string are compiled and matched by your own browser and are never uploaded, logged or sent to a server, so you can paste a real log excerpt without scrubbing it first. The tool does keep your last five pattern and text pairs in the localStorage of this browser so you can restore them, and the Clear button in that panel deletes them.
Sources
- MDN, Regular expressions guide and the RegExp reference (flags, named groups, lookbehind, Unicode property escapes,
lastIndex, and the per browser support tables). - Python Software Foundation, the
remodule documentation (the(?P<name>...)spelling and there.ASCIIflag). - Google, RE2 syntax (the engine behind Go and Rust, and what it deliberately leaves out).
- OWASP, Regular expression Denial of Service (how nested quantifiers turn into an outage).
- IETF, RFC 5322 for email address syntax and RFC 3339 for the timestamp shape used by the ISO 8601 sample pattern.
- Command line behavior comes from the POSIX specification for
grepandsedplus the GNU and BSD manual pages; database support is described in each vendor documentation and changes between major versions.
More tools
Patterns rarely travel alone. When the text you are matching is a config or an API response, the JSON formatter makes the structure visible first, which often shows that you wanted a key lookup rather than a regex. When a pattern has to go into a query string, the URL encoder shows what the server will actually receive. Log lines usually start with a Unix timestamp worth converting before you match around it, and the character counter is the quick way to check how long a captured field really is.
Related tools
- JSON Formatter and Validator
Pretty-print or minify JSON in your browser with 2-space, 4-space or tab indentation.
- URL Encoder and Decoder
Percent-encode or decode URLs and query strings with encodeURI or encodeURIComponent semantics.
- Base64 Encode and Decode
Convert text to Base64 and back with full UTF-8 support, so accented letters, CJK and emoj…
- Unix Timestamp Converter
Convert Unix epoch timestamps to human dates and back, in your local time zone and UTC.
- Word Counter & Character Count
Count words, characters with and without spaces, sentences and paragraphs as you type.
Privacy: the pattern and your test text are compiled and matched entirely in your browser and never sent to a server, stored on one or logged. The last five pattern and text pairs stay in the localStorage of this browser for the history panel and can be cleared from there. This tool sends no analytics event of its own, and the result area is excluded from session recording, so neither the pattern nor the text is collected.
