Regex Lookahead Explained: Positive and Negative Lookahead Examples
What lookahead actually does
Lookahead lets you say: match this, but only if what comes next (or doesn't come next) matches another pattern. It's a zero-width assertion — it checks a condition without consuming any characters. Think of it as a boundary test that runs before the real match.
Positive lookahead: (?=...)
Positive lookahead means the main pattern must be followed by something specific. The classic example is matching passwords that contain at least one digit. The regex looks like this: ^(?=.*\d).{8,}$
Breaking it down: ^ anchors to the start. (?=.*\d) says "ahead of the current position, there must be zero or more of any character followed by a digit." The .{8,} matches at least eight characters. Without the lookahead, you'd have to write several alternatives to cover every position the digit could appear in.
Negative lookahead: (?!...)
Negative lookahead flips it: match this only if what follows does NOT match the inner pattern. Common use: exclude a specific file type. ^.*\.(?!exe$|dll$).+$ matches any filename that doesn't end in .exe or .dll.
Another practical case: finding repeated words. \b(\w+)\b\s+(?!\1\b)\1\b catches the second occurrence of a repeated word — the negative lookahead prevents matching the first word twice in a row.
Walk through a password example
Here's a fuller password regex that requires at least one uppercase, one lowercase, and one digit: ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$
Each lookahead runs independently at the start of the string. The first checks for a lowercase letter somewhere ahead. The second checks uppercase. The third checks a digit. After all three pass, .{8,} does the actual length match. Read it as a series of gates, not a linear scan.
File extension filter example
Say you want to list all images but skip thumbnails. ^(?!.*thumb).*\.(png|jpg|webp)$ matches image files but excludes anything with "thumb" in the name. The negative lookahead checks the whole path before the extension match runs.
Engine compatibility
Lookahead is supported in JavaScript, Python, Java, Go, and PHP. The one place you'll hit trouble is old POSIX ERE and some legacy tools. Also note: JavaScript doesn't support lookbehind (?<=...) in very old engines, but lookahead has always worked. If you're targeting modern environments, you're fine.
Common mistake
People put the lookahead in the wrong position. The assertion runs from where the main pattern currently sits. If you write a.*(?=\d), it matches up to the first digit — the lookahead doesn't search the whole string unless you add .* inside it. Position matters.