The pipe | means “or” — it lets a single pattern match one of several alternatives. It’s one of the simplest pieces of regex syntax, and also one of the easiest to get subtly wrong, because | has lower precedence than almost everything else in a pattern.
The basics
Alternation tries each option, separated by |, until one matches:
/cat|dog|bird/.test("I have a dog"); // true
/cat|dog|bird/.test("I have a fish"); // false
You can combine alternation with grouping to apply it to just part of a pattern — for example, matching either “gray” or “grey”:
/gr(?:a|e)y/.test("gray"); // true
/gr(?:a|e)y/.test("grey"); // true
The precedence gotcha
Here’s where it gets tricky. | doesn’t just apply to the option immediately next to it — it splits the entire pattern at that point, unless you scope it with a group. This means ^cat|dog$ is not “match a line that’s cat or dog” — it’s really (^cat)|(dog$): match “cat” at the start of a line, or, completely independently, match “dog” at the end of a line.
Look closely at the matches: "catalog" matches too, even though it’s clearly not the word “cat” on its own — because ^cat only requires “cat” at the start of the line, with nothing said about what comes after. Meanwhile "dogma" matches nothing, because dog$ requires “dog” at the very end of the line, and “dogma” ends in “ma”.
Now compare that to the same alternatives, properly scoped inside a non-capturing group so ^ and $ apply to the whole thing:
This version only matches lines that are exactly “cat” or “dog” — “catalog” and “cat nap” are correctly excluded now, because the anchors wrap the entire alternation instead of just one branch of it.
// Looks like it means "start or end with cat/dog" — it doesn't
/^cat|dog$/
// Actually means "the whole line is exactly cat or dog"
/^(?:cat|dog)$/
Watch out
Whenever you mix | with ^, $, or any assertion, ask yourself: does this anchor apply to just the neighboring alternative, or to the whole group? If in doubt, add explicit parentheses — it costs nothing and removes all ambiguity.
Ordering matters when one alternative is a prefix of another
Alternation tries options left to right and stops at the first one that lets the rest of the pattern succeed — it does not search for the longest possible match. If a shorter alternative is a prefix of a longer one and is listed first, the longer one may never get a chance:
Even though the full word “catalog” is right there in the text, the pattern only ever matches "cat" — the first alternative succeeds immediately, so the engine never tries "catalog". Flip the order and the longer alternative wins when it can:
| Pattern | Against “catalog” | Why |
|---|---|---|
cat|catalog |
Matches "cat" only |
Shorter alternative tried first, succeeds, engine stops |
catalog|cat |
Matches "catalog" |
Longer alternative tried first, succeeds, captures the whole word |
Fix the precedence bug
You want a pattern that matches a line that is exactly "yes" or exactly "no" — nothing else. A teammate wrote ^yes|no$. Explain why it’s wrong, and fix it.
Show solution
^yes|no$ is really (^yes)|(no$) — it matches “yes” at the start of a line (even as part of a longer word like “yesterday”) or “no” at the end of a line (even as part of “casino”), independently of each other.
The fix is to scope the alternation with a group so both anchors apply to the whole thing:
/^(?:yes|no)$/Now the pattern only matches a line that is exactly “yes” or exactly “no”, nothing more and nothing less.
What’s next
Alternation matches one of several fixed options. Next, you’ll learn lookahead — a way to check what comes next in the string without actually matching (or consuming) it.