Regular Expressions Basics: Flags, Groups and Pitfalls
A plain guide to regular expressions in JavaScript: the flags, capture groups, named groups, lookahead, and how nested repeats can make a pattern freeze.
In this article
A regular expression is a short pattern that describes a set of strings. It lets you find, check or pull out parts of text with one line of code. This guide covers the building blocks in JavaScript, the flags that change how a pattern runs, groups for extracting pieces, and the one big pitfall: patterns that can take an enormous time to fail.
The building blocks
- Literal characters match themselves: cat matches cat.
- A dot matches any one character except a line break, unless the s flag is on.
- \d matches a digit, \w a letter, digit or underscore, \s a space, and the capital forms mean the opposite.
- Square brackets give a choice: [aeiou] matches one vowel, and [0-9a-f] matches one hex digit.
- Quantifiers repeat: * for zero or more, + for one or more, ? for optional, and {2,4} for two to four.
- ^ and $ anchor the start and end of the text, or of each line with the m flag.
- A vertical bar means or: cat|dog.
A date pattern such as \d{4}-\d{2}-\d{2} reads as four digits, a hyphen, two digits, a hyphen and two digits. It matches 2026-10-31 but would also match 9999-99-99, because a pattern checks shape, not whether a date exists.
Flags
Flags follow the pattern and change how it is applied. The g flag finds all matches instead of stopping at the first. The i flag ignores upper and lower case. The m flag makes the start and end anchors work on each line. The s flag lets the dot match line breaks. The u flag treats the text as Unicode code points and allows property escapes. The y flag makes a match only at the current position, and d returns the start and end of each group. Flags can be combined in any order but cannot be changed after the expression is created.
Capture groups
Brackets capture the part of the text they match. Applying (\d{4})-(\d{2}) to 2024-03 gives the whole match 2024-03 plus group 1 as 2024 and group 2 as 03. A group written (?:...) groups without capturing. A group can be given a name by putting a question mark and the name, in angle brackets, straight after the opening bracket. The result is then reached by name, as groups.year, which is easier to read than a number. A backreference such as \1 requires the same text to appear again, which is how a pattern can insist on the same separator twice.
Lookahead
A lookahead checks what follows without including it in the match. The pattern \d+(?= dollars) matches the number in I have 50 dollars, but the match is only 50. The negative form (?!...) matches only when the text does not follow. These are useful for rules such as a number that is not followed by a percent sign.
The pitfall: catastrophic backtracking
Most regular expression engines, including the one in the browser, try one path through the pattern and go back to try another when it fails. Usually that is quick. But a pattern with a repeat inside another repeat, such as (a+)+$, can split a run of letters into groups in a huge number of ways. When the text ends in a character that does not fit, the engine must try all of them before it gives up. OWASP counts 16 paths for four letters and 65,536 for sixteen, doubling with each letter added. This is called a regular expression denial of service, or ReDoS. On a server, a single crafted input can occupy a processor for minutes.
Warning signs are a group that is repeated and contains another repeat, or alternatives that overlap inside a repeat, such as (a|aa)+. The usual fixes are to make the inner part unambiguous, to avoid nesting repeats, and to limit the length of the text before matching. The tester on this site runs each pattern in a background worker and stops it after about a second and a half, so a bad pattern cannot freeze the page.
Practical tips
- Start simple and test with both good and bad examples.
- Anchor a validation pattern with ^ and $, or it will match a part of the text.
- Prefer specific classes such as [^,]* over a greedy dot.
- Use named groups when there are more than two.
- Do not use a regular expression to check an email address or parse HTML fully; use a purpose-built tool.
- Limit the size of text before running an untrusted pattern or running a pattern on untrusted text.
Sources
Share
Keep reading
Related articles
UUIDs Explained: Versions 4 and 7 and When to Use Them
What a UUID is, how version 4 and version 7 are built, why collisions are not a worry in practice, and why a UUID must not be used as a secret.
URL Encoding Explained: What %20 and %3D Mean
Why URLs need percent-encoding, which characters are safe, how encodeURI and encodeURIComponent differ, and how to fix the most common double-encoding and plus-sign mistakes.
What Is Base64? How to Encode and Decode It
What Base64 is for, how 3 bytes become 4 characters, why the text grows by a third, the URL-safe form and the common mistakes, including thinking it is encryption.