Developers

How to Test a Regular Expression Online (With Real Patterns)

7 min read

To test a regular expression, write the pattern, choose the flags it needs, run it against a realistic test string, and check both the highlighted matches and any captured groups before that pattern goes anywhere near production code. Skipping the test step is how a “simple” email check ends up rejecting real addresses or, worse, accepting garbage.

Pattern, flags, and capture groups

A regex pattern sits between two forward slashes, /pattern/, the same convention JavaScript uses natively. Everything between those slashes is the pattern itself: literal characters, character classes like \d or [a-z], quantifiers like + or {2,}, and anchors like ^ and $.

Flags change how that pattern behaves, and there are exactly four worth knowing:

  • g (global) finds every match in the text instead of stopping after the first one.
  • i (case-insensitive) treats uppercase and lowercase letters as equivalent.
  • m (multiline) makes ^ and $ match the start and end of each line, not just the start and end of the whole string.
  • s (dotAll) lets the dot . match newline characters too, which it otherwise skips.

Parentheses () inside a pattern create a capture group, a piece of the match you can pull out on its own. (\d{4}) captures four digits as a separate value alongside the full match. Named groups work the same way but give that piece a label instead of a number, (?<year>\d{4}) captures four digits into a group called year, which is easier to read back later than “group 3.”

Four patterns worth knowing

PatternFlagsMatchesDoesn’t matchCaptures
^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$none[email protected]jane@example (no top-level domain)none
#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})g#2F7BFF, #fff#12G456 (G is not hex)Group 1: 2F7BFF
\/(\w+)\/(\w[\w-]*)g/users/123, /products/abc-456/users (no second segment)Group 1: resource type, Group 2: id
^\d{4}-\d{2}-\d{2}$none2026-07-1107/11/2026 (wrong format)none

Two of these deserve a closer look, because the reasoning behind them shows up in real code constantly.

Take the REST-path pattern, \/(\w+)\/(\w[\w-]*), run with the g flag against a string like /users/123, /products/abc-456. The literal \/ matches a forward slash (escaped so the regex engine doesn’t confuse it with the closing delimiter). (\w+) then captures one or more word characters, letters, digits, or underscores, which grabs users or products, the resource type. Another \/ matches the next slash, and (\w[\w-]*) captures the id: it has to start with a word character, then allows more word characters or hyphens after that, which is why abc-456 matches in full instead of stopping at the hyphen. Run without g, this pattern would only report /users/123 and quietly ignore /products/abc-456 even though it’s sitting right there in the same string. With g, the tool returns both matches, each with its own group 1 and group 2, which is exactly what you’d feed into a router that needs to know “this URL is a products lookup for id abc-456” as distinct fields rather than one undifferentiated string.

The hex-color pattern, #([0-9a-fA-F]{6}|[0-9a-fA-F]{3}), is built to catch both long and short CSS color notation in one pass. The # is literal. Inside the parentheses, the | is an alternation: either six hex digits ([0-9a-fA-F]{6}, matching something like 2F7BFF) or exactly three (matching the shorthand fff). Because the whole alternation sits inside (), the matched digits land in group 1 regardless of which branch fired, so #2F7BFF captures 2F7BFF and #fff captures fff using the same group index. The g flag is what turns this from a single lucky hit into something actually useful: scanning a CSS file for every #2F7BFF, #fff, and any other hex color in the document, not just the first one the engine happens to find. Pull group 1 out of each match and you have a de-duplicated list of every brand color used across a stylesheet, which is a genuinely common thing to need when auditing a design system for drift.

Test it yourself

Paste your own pattern and test string below and watch the matches and capture groups update as you type.

/ /
Regex Tester
Free, no sign-up, works on any device.
Open the full tool

Common mistakes and edge cases

Forgetting the g flag. Without it, the regex engine stops after the very first match, even if the text contains ten more. This silently breaks anything that expects every occurrence, most obviously a find-and-replace-all operation that only ends up replacing the first hit and leaving the rest untouched.

The unescaped dot. A bare . in a regex matches any single character, not a literal period. Write 192.168.1.1 as a pattern and it will happily match 192X168X1X1 too, because each dot is standing in for “anything.” If you mean a literal dot, escape it: 192\.168\.1\.1.

Greedy vs lazy quantifiers. .* is greedy by default, meaning it grabs as much text as it possibly can before backing off. Against a block of HTML, a pattern meant to capture one tag can end up matching from the first < all the way to the very last > in the document, swallowing everything in between instead of stopping at the first tag’s close. Adding a ? after the quantifier, .*?, makes it lazy: it stops at the first point where the rest of the pattern can still match, which is usually what you actually wanted.

Missing anchors. Without ^ and $, a pattern is free to match anywhere inside the text, as a substring, not the whole string. A “validation” pattern with no anchors can pass a string that merely contains a valid-looking fragment somewhere in the middle, letting bad input slip through a check that looked airtight. Adding ^ at the start and $ at the end forces the entire string to satisfy the pattern, with nothing extra before or after.

Frequently asked questions

What’s the actual difference between using the g flag and leaving it off? Without g, the regex engine returns only the first match it finds and stops. With g, it keeps scanning after each match and returns all of them. This matters most for anything that touches multiple occurrences: replace-all operations, counting how many times a pattern appears, or extracting a full list of values (like every hex color in a stylesheet) instead of just the first one.

How do I match a literal special character like a dot, dollar sign, or parenthesis? Escape it with a backslash: \. for a literal dot, \$ for a literal dollar sign, \( and \) for literal parentheses. Regex reserves these characters for pattern syntax (. means “any character,” $ means “end of string,” () opens a group), so the backslash tells the engine to treat the character as plain text instead.

What’s a non-capturing group, (?:...), and when do I need one instead of a normal (...) group? A non-capturing group applies grouping and alternation without adding an entry to the captured results. Use (?:...) when you need parentheses purely for structure, say, grouping an alternation like (?:png|jpg|gif) inside a longer pattern, but you don’t actually need that piece of text pulled out afterward. It keeps your group numbering clean when you do have other groups you care about capturing.

Does this tool send my pattern or test text anywhere? No. The Regex Tester runs entirely in your browser using JavaScript’s native RegExp engine. Nothing you type, neither the pattern nor the test text, is sent to a server at any point.

RegexValidationDevelopersRegular Expressions
Regex Tester
Now try it yourself with the full tool.
Try it now