Regex Tester

|

//
Matches

Enter a pattern above to see matches.

Enter a JavaScript regular expression and test it against sample text. Matches highlight in real time with match index, captured groups, and count — using the same RegExp engine as Node.js and all modern browsers.

Testing patterns before using them in code

A pattern that passes a mental check on paper often fails against real input: a greedy quantifier captures too much, a missing flag causes case mismatch, an anchor placement changes which lines match.

Paste the actual strings your code will process — log lines, user input, API responses — rather than constructed examples. Edge cases show up in real data, not in simple test strings you write yourself.

ISO date/\d{4}-\d{2}-\d{2}/g → matches "2024-03-15" in "Event on 2024-03-15"
Hex color/#[0-9a-fA-F]{6}/gi → matches "#FF5733", "#abc123"
Greedy vs lazy/<.+>/ → "<b>text</b>" as one match; /<.+?>/g → each tag separately

Flags

Flags change how the entire pattern behaves and are placed after the closing slash: /pattern/flags. Multiple flags can be combined freely.

g (global) — return all matches, not just the first. Without g, exec() and match() stop after the first match regardless of how many times the pattern appears.

i (case-insensitive) — treat uppercase and lowercase as equivalent. /error/i matches "Error", "ERROR", and "error".

m (multiline) — ^ and $ match the start and end of each line, not just the whole input. Without m, /^\w+/ only matches a word at the absolute start of the input.

s (dotAll) — . matches any character including \n. Without s, . skips newlines, so /start.*end/ fails when there is a line break between "start" and "end".

u (unicode) — full Unicode code-point support and stricter escape sequence parsing. Use when matching emoji or characters outside the Basic Multilingual Plane.

g — all matches/\d+/g on "a=1, b=22, c=3" → ["1", "22", "3"] (not just "1")
i — case blind/error/gi → matches "Error", "ERROR", "error" in the same string
m — per-line anchors/^\d+/mg on "1: first\n2: second" → ["1", "2"]
s — dot crosses newlines/BEGIN.*END/s → matches "BEGIN\nmiddle\nEND" as one match

Capture groups and non-capturing groups

Parentheses (…) create a numbered capture group. When the pattern matches, each group's content is shown in the results panel labeled g1, g2, etc. Use capture groups to extract specific parts of a match — year, month, and day from a date string, for example, or a log level and message from a log line.

Named groups use the syntax (?<name>…) and behave identically to numbered groups but are identified by a label rather than an index.

Non-capturing groups (?:…) group tokens for quantifiers or alternation without creating a capture entry. Use (?:jpg|png|gif) when you want to match any of those three values but do not need to capture which one matched. Non-capturing groups keep the numbered index count lower and the match output cleaner.

This tool uses the JavaScript RegExp engine. Patterns from Python (re module), PCRE, or Java may use syntax not valid in JavaScript — atomic groups (?>) and possessive quantifiers (++) are not supported here.
Numbered groups/(\d{4})-(\d{2})-(\d{2})/ on "2024-03-15" → g1="2024", g2="03", g3="15"
Named groups/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/ → same extraction, named
Extract from log line/\[(\w+)\] (.+)/ on "[ERROR] Timeout" → g1="ERROR", g2="Timeout"
Non-capturing alternation/(?:jpg|png|gif)$/i → matches extension without creating a capture

Greedy vs lazy matching

Quantifiers (*, +, ?, {n,m}) are greedy by default: they match as many characters as possible while still allowing the overall pattern to succeed. This causes them to capture more than expected when the same character appears multiple times in the string.

Adding ? after a quantifier (*?, +?, ??, {n,m}?) makes it lazy: it matches as few characters as possible and expands only as far as necessary. The pattern still has to succeed — it just stops at the earliest opportunity.

Classic example: /<.+>/ on "<b>bold</b> and <em>italic</em>" matches the entire string from the first < to the last >. The greedy .+ consumes all the way to the end and backs off only as far as it must to find a final >. Switch to /<.+?>/g to match each individual tag instead.

Greedy — whole span/<.+>/ on "<b>text</b>" → one match: "<b>text</b>"
Lazy — each tag/<.+?>/g on "<b>text</b>" → ["<b>", "</b>"]
Greedy string capture/".*"/ on 'a": "val1", "b": "val2"' → matches from first to last "
Lazy string capture/".*?"/g → matches each individual quoted token separately

Escaping special characters

Twelve characters have special meaning in regex syntax and must be escaped with a backslash to match them literally: . * + ? ^ $ { } [ ] ( ) | and \ itself.

The dot (.) is the most commonly forgotten escape. Without escaping, /3.14/ matches "3.14" but also "3X14" or "3 14" — any character in that position. The pattern /3\.14/ matches only the literal string "3.14".

In JavaScript regex literals (/pattern/), forward slashes must also be escaped (\/) because an unescaped slash ends the pattern. In new RegExp('string'), each backslash needs double escaping because the string itself consumes one level: write '\\d' to get the regex \d.

Version number/\d+\.\d+\.\d+/g → matches "1.2.3" exactly, not "1X2Y3"
File extension/\.json$/i → literal dot before "json"; without \ matches any char
URL protocol/https?:\/\// → matches "http://" or "https://"
Price/\$\d+\.\d{2}/g → matches "$9.99", "$120.00"

Character classes, anchors, and shorthands

\d matches any digit 0–9. \w matches any word character (letters, digits, underscore — equivalent to [a-zA-Z0-9_]). \s matches whitespace: space, tab, newline, carriage return, and form feed. Each has an uppercase inverse: \D (non-digit), \W (non-word), \S (non-whitespace).

\b is a word boundary — a zero-width assertion at the position between a word character and a non-word character. /\bcat\b/ matches "cat" as a standalone word but not "catch", "scat", or "concatenate". It costs nothing in terms of characters consumed.

^ matches the start of the string. $ matches the end. Add the m (multiline) flag and they match the start and end of each line instead. Without m, ^ and $ only match the very beginning and very end of the full input.

\d+ (one or more digits)/\d+/g → "Order 123, qty: 4" matches ["123", "4"]
\w+ (word characters)/\w+/g → "hello, world!" matches ["hello", "world"]
\b word boundary/\bcat\b/g → matches "cat" in "the cat sat" but not in "catch"
^ start (multiline)/^\d+/mg → matches leading digit sequences on each line
$ end/\.json$/i → matches paths ending in ".json" or ".JSON"

Practical patterns for common developer tasks

These patterns cover tasks that come up frequently when processing logs, validating inputs, or extracting values from text. Test each against your actual data before using in production — edge cases in real strings often break patterns that look correct on a simple example.

UUID v4/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi
ISO date YYYY-MM-DD/\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])/g
Basic email shape/^[^@\s]+@[^@\s]+\.[^@\s]+$/ (structural check, not RFC 5321)
IPv4 address/\b(?:\d{1,3}\.){3}\d{1,3}\b/g
HTTP method + path in logs/(?:GET|POST|PUT|PATCH|DELETE)\s\/\S+/g

Debugging: patterns that match too much, too little, or nothing

Matches nothing: the most common cause is a case mismatch — /error/ does not match "Error"; add the i flag. The second most common cause is an anchor without the m flag: /^status:/g only matches if "status:" appears at the very start of the entire input; with multi-line text it misses every line except the first. A wrong character class — using [A-Z] when the input is lowercase — also returns zero matches.

Matches too much: a greedy quantifier (.* or .+) expands to the farthest possible position before backing off. The fix is to switch to lazy (*? or +?) or add a more specific character class instead of a dot. Also check whether missing ^ and $ anchors allow the pattern to match a substring inside a longer word.

Matches too little (stops after the first occurrence): this is almost always a missing g flag. Without g, exec() and match() return only the first match. If you are using .exec() in a loop and the pattern lacks g, lastIndex never advances and the loop runs forever. Add g to get all occurrences.

Missing i → no match/error/ → misses "Error"; fix: /error/i
Missing m → wrong anchor/^id:/ → only matches first line; fix: /^id:/m
Greedy overreach/<.+>/g → "<a>x</a>" is one match; fix: /<.+?>/g
Missing g → only first/\d+/ on "1 2 3" → returns ["1"] only; fix: /\d+/g

Frequently Asked Questions

Why does my pattern not match anything?
The three most common causes: (1) case mismatch — /error/ does not match "Error"; add the i flag. (2) Missing m flag — /^\w+/ only matches a word at the very start of the entire input, not at the start of each line; add m for per-line anchoring. (3) Wrong character class — [A-Z] only matches uppercase; [a-zA-Z] matches both cases.
What is the difference between greedy and lazy quantifiers?
Greedy quantifiers (*, +, {n,m}) match as many characters as possible. Lazy versions (*?, +?, {n,m}?) match as few as possible. Example: /<.+>/ on "<b>text</b>" matches the whole string including the closing tag. /<.+?>/ matches only "<b>". Use lazy quantifiers when your pattern captures more context than you intended. Switching .* to .*? is usually the first thing to try.
Does it support named capture groups?
Yes. The syntax is (?<name>…). The tool shows results by group index (g1, g2, etc.) in the results panel. Named groups work identically to numbered groups in the match output — they are an authoring convenience that makes patterns easier to read, not a change in matching behavior.
What does (?:...) do differently from (...)?
Both group tokens and can be quantified or used for alternation. The difference is that (...) creates a numbered capture group whose content appears in the match results. (?:...) is a non-capturing group — it groups without storing. Use (?:jpg|png|gif) when you need alternation but do not need to capture which option matched. This keeps the numbered group index count lower and the match output uncluttered.
How do I match a literal dot, parenthesis, or other special character?
Escape it with a backslash. A bare dot means "any character"; \. means a literal period. The full list of characters that need escaping in JavaScript regex: . * + ? ^ $ { } [ ] ( ) | \. In new RegExp('string'), each backslash must be doubled because the string itself consumes one level of escaping: write '\\.' to produce the regex \.
My pattern works in Python but not here — why?
Python's re module and PCRE support features not in the JavaScript RegExp engine: atomic groups (?> ), possessive quantifiers (++, *+), \A and \Z anchors (start/end of string only, regardless of flags), and verbose mode (re.VERBOSE). Translate the pattern to JavaScript syntax before testing here. Most common patterns translate directly; the differences mainly affect advanced backtracking control.
What are lookaheads and lookbehinds?
Zero-width assertions that check context without consuming characters. (?=...) is a positive lookahead (position must be followed by the pattern); (?!...) is negative. Lookbehinds: (?<=...) and (?<!...). Example: \w+(?=@) matches the username part of an email address without including @ in the match. All four are supported in the JavaScript RegExp engine used here.

Related Tools