JavaScript RegExp (?!...) Negative Lookahead

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Zero-width assertion

What You’ll Learn

The (?!...) syntax is a negative lookahead assertion. It lets you match text only when it is not followed by a specific pattern—without consuming the characters you peek at. It is zero-width, powerful for exclusions, and a stepping stone into advanced regex.

01

(?!...)

Not followed by

02

Zero-width

Peek, don’t eat

03

Assertion

Not + or *

04

Exclude

Filter suffixes

05

vs (?=)

Positive twin

06

Validation

Password rules

Introduction

Sometimes you need to match part of a string while rejecting certain what-comes-next situations. For example, match foo but not when it is immediately followed by bar. You could try long alternation lists, but negative lookahead expresses the rule directly: /foo(?!bar)/.

Old tutorials often call (?!...) a “quantifier,” but that name is misleading. Quantifiers like + and * repeat tokens. Lookahead is an assertion: a condition on upcoming text that does not become part of the final match. Think of it as peeking ahead and saying “only continue if the next characters are not what I forbid.”

Understanding (?!...) Negative Lookahead

Syntax: (?!pattern) where pattern is any valid regex sub-expression. When the engine reaches this point, it checks whether the text starting at the current position matches pattern. If it does, the assertion fails; if it does not, the assertion succeeds and matching continues.

JavaScript
const samples = ["foobar", "foobaz", "foo"];

samples.forEach((s) => {
  console.log(s, s.match(/foo(?!bar)/));
});
// foobar null
// foobaz ["foo"]
// foo    ["foo"]

In "foobar", foo is followed by bar, so (?!bar) fails. In "foobaz", the next characters are baz, not bar, so the match succeeds and returns only "foo"—the lookahead text is not included.

💡
Beginner Tip

Read (?!bar) aloud as “not followed by bar.” The matched result never includes the lookahead portion—only the part before it.

How to Use Negative Lookahead

Place (?!...) immediately after the text you want to conditionally match, or at the start of a pattern to reject entire strings:

JavaScript
/foo(?!bar)/;                       // foo not followed by bar
/\d+(?! dollars)/g;                 // numbers not followed by " dollars"
/^(?!.*password).{8,}$/i;          // 8+ chars, no "password"
new RegExp("foo(?!bar)");           // constructor form

"http://a.com".match(/http(?!s)/); // ["http"] — not https

Combine with the global flag when you need every qualifying match in a string. Use anchors (^, $) inside lookaheads for whole-string validation rules.

📝 Syntax

JavaScript
(?!pattern)          // negative lookahead — must NOT follow
(?=pattern)          // positive lookahead — MUST follow (contrast)
(?!foo|bar)          // not followed by foo OR bar
^(?!.*badword).+$    // whole string must not contain badword
token(?!suffix)      // token only when suffix absent

Common patterns

  • /foo(?!bar)/ — match foo unless bar follows.
  • /http(?!s)/ — match http but not https.
  • /\d+(?! dollars)/g — numbers not immediately followed by dollars.
  • /^(?!.*@spam\.com$).+@.+/ — emails not from blocked domain.

⚡ Quick Reference

GoalPattern / API
Not followed by text/part(?!bad)/
Exclude https/http(?!s)/
Exclude suffix globally/\d+(?! dollars)/g
Reject forbidden substring/^(?!.*word).+$/
Positive counterpart(?=pattern)
Constructor formnew RegExp("a(?!b)")
Does not consume inputZero-width — not in match result

📋 Negative vs Positive Lookahead

(?!...) forbids what follows; (?=...) requires it.

Negative
(?!bar)

Must NOT be followed by bar

Positive
(?=bar)

MUST be followed by bar

On "foobaz"
/foo(?!bar)/

Matches foo

On "foobar"
/foo(?!bar)/

No match — bar follows

Examples Gallery

Open DevTools Console (F12) or use Try-it links. Examples cover basic exclusion, number filtering, password rules, domain blocking, and side-by-side comparison with plain matching.

📚 Getting Started

Match text only when a forbidden suffix is absent.

Example 1 — Match foo Not Followed by bar

Use /foo(?!bar)/ to exclude the foobar case.

JavaScript
const words = ["foobar", "foobaz", "foo"];

words.forEach((w) => {
  console.log(w, w.match(/foo(?!bar)/));
});
// foobar null
// foobaz ["foo"]
// foo    ["foo"]
Try It Yourself

How It Works

After matching foo, the engine peeks ahead. If bar is next, the assertion fails and the match is rejected. The returned match is only foo, never the lookahead text.

📈 Practical Patterns

Filtering, validation, and real-world exclusions.

Example 2 — Match http But Not https

Distinguish insecure URLs from secure ones with /http(?!s)/.

JavaScript
const urls = [
  "http://example.com",
  "https://secure.com",
  "http://legacy.org"
];

urls.forEach((url) => {
  console.log(url, url.match(/http(?!s)/));
});
// http://example.com  ["http"]
// https://secure.com  null
// http://legacy.org   ["http"]
Try It Yourself

How It Works

After matching http, the engine checks whether s comes next. For https, the assertion fails, so the pattern does not match. For plain http, there is no trailing s, so the match succeeds.

Example 3 — Reject Passwords Containing password

Use a start-of-string lookahead to forbid a substring anywhere in the value.

JavaScript
const rule = /^(?!.*password).{8,}$/i;

console.log(rule.test("MySecret1!"));        // true
console.log(rule.test("short"));             // false — too short
console.log(rule.test("myPassword123"));     // false — contains "password"
Try It Yourself

How It Works

^(?!.*password) checks from the start that nowhere in the string does password appear. Then .{8,}$ enforces minimum length. This pattern validates the whole input, not just a substring.

Example 4 — Block Emails from a Domain

Accept email-shaped strings except those ending in @spam.com.

JavaScript
const allowed = /^(?!.*@spam\.com$)[^\s@]+@[^\s@]+\.[^\s@]+$/;

console.log(allowed.test("user@example.com"));  // true
console.log(allowed.test("bot@spam.com"));      // false
console.log(allowed.test("not-an-email"));      // false
Try It Yourself

How It Works

The negative lookahead at the start rejects any string containing @spam.com at the end. The rest of the pattern checks a simplified email shape. Real email validation needs more rules, but the lookahead cleanly expresses the exclusion.

Example 5 — With vs Without Negative Lookahead

See how adding (?! dollars) changes which numbers match.

JavaScript
const text = "5 dollars and 10 coins";

console.log(text.match(/\d+/g));              // ["5", "10"]
console.log(text.match(/\d+(?! dollars)/g));  // ["10"]
Try It Yourself

How It Works

Plain \d+ grabs every number. Adding (?! dollars) skips 5 because it is followed by a space and dollars. Only 10 survives the filter.

🚀 Common Use Cases

  • Protocol checks — match http but exclude https, or similar prefix rules.
  • Password / input rules — forbid dictionary words or forbidden substrings anywhere in a value.
  • Content filtering — block emails, URLs, or IDs from deny lists with lookahead at ^.
  • Parser guards — distinguish similar syntax (e.g., foo vs foobar) without long alternation.
  • Search refinement — find numbers, words, or codes that are not part of a larger known pattern.
  • Password-free validation — combine multiple lookaheads for complex AND/OR rules on one string.

Important Considerations

  • Zero-width — matched text never includes the lookahead portion; only the part before it appears in results.
  • Not a quantifier(?!...) does not repeat characters; it adds a condition on what follows.
  • Lookbehind counterpart — JavaScript also has (?<!...) negative lookbehind (ES2018), which checks text before the position instead of after.
  • Performance — nested or many lookaheads on huge strings can slow matching; keep patterns focused.
  • Anchor placement^(?!.*bad) validates the whole string; mid-pattern (?!bad) checks only what comes next.
  • Constructor escaping — in new RegExp() strings, backslashes need doubling: "(?!\\\\d)".

🧠 How Negative Lookahead Works

1

Match preceding part

The engine matches literal text or groups before (?!...).

Pattern
2

Peek ahead

At the current position, try to match the inner pattern without advancing the cursor.

Lookahead
3

Invert the result

If inner pattern matches, assertion fails. If it does not match, assertion succeeds.

Assert
4

Continue or backtrack

On success, matching continues. On failure, the engine backtracks and tries other paths.

📝 Notes

  • Positive counterpart: (?=...) (covered on the quantifier-positive page when published).
  • Previous topic: $ end anchor.
  • Next topic: + one-or-more quantifier.
  • Lookahead differs from the $ anchor, which only checks end-of-string/line.
  • For “must contain” rules, positive lookahead (?=.*digit) is the usual pattern.
  • See (x|y) alternation when you need to list allowed forms instead of forbidding one.

Browser & Runtime Support

Negative lookahead (?!...) is part of core JavaScript regular expression syntax.

Baseline · ES3+

RegExp (?!...) lookahead

Supported in every browser and Node.js version. No polyfill needed for negative lookahead assertions.

99% Universal syntax
Google Chrome Supported · Desktop & Mobile
Full support
Mozilla Firefox Supported · Desktop & Mobile
Full support
Apple Safari Supported · macOS & iOS
Full support
Microsoft Edge Supported · Chromium
Full support
Internet Explorer No native support · Use a polyfill
Polyfill
Opera Supported · Modern versions
Full support
Samsung Internet Supported · Android
Full support
Bun Supported · JavaScript runtime
Supported
Deno Supported · JavaScript runtime
Supported
Node.js Supported · Server runtime
Supported
Android WebView Supported · Modern WebView
Full support
(?!...) Excellent

Bottom line: Safe everywhere. Use (?!...) when you must exclude specific following text without consuming it.

Conclusion

The (?!...) negative lookahead lets you match text only when a forbidden pattern does not follow. It is zero-width, precise, and ideal for exclusions—from skipping https when matching http to blocking weak passwords and spam domains.

Remember it is an assertion, not a quantifier like + or *. Pair it with anchors for whole-string rules, use the global flag to find every match, and contrast it with (?=...) when you need the opposite check. Next, learn the + one-or-more quantifier.

💡 Best Practices

✅ Do

  • Read (?!x) as “not followed by x”
  • Use ^(?!.*forbidden) to ban substrings in whole inputs
  • Add g when filtering every occurrence in a string
  • Keep inner lookahead patterns as short and specific as possible
  • Test edge cases where the forbidden text almost matches

❌ Don’t

  • Expect lookahead text to appear in the match result
  • Call (?!...) a repetition quantifier
  • Over-nest lookaheads without testing readability
  • Forget to escape dots in domains: @spam\.com
  • Replace clear alternation when a simple | list is enough

Key Takeaways

Knowledge Unlocked

Five things to remember about (?!...) negative lookahead

Exclude unwanted following text without consuming it.

5
Core concepts
👁 02

Zero-width

Peek only.

Concept
📈 03

Assertion

Not + or *.

Type
🔒 04

^(?!.*x)

Ban substring.

Validate
🔄 05

(?=...)

Opposite rule.

Compare

❓ Frequently Asked Questions

(?!...) is a negative lookahead assertion. It succeeds when the text immediately after the current position does NOT match the pattern inside the parentheses. It is zero-width—it checks ahead without consuming characters.
No. Although some tutorials label it a quantifier, (?!...) is an assertion (a condition on what follows). Quantifiers like +, *, and ? repeat or optionalize the previous token. Lookahead checks the next characters without including them in the match result.
(?=...) is positive lookahead—it requires the following text to match. (?!...) is negative lookahead—it requires the following text NOT to match. Both are zero-width and do not consume input.
Yes. Write it inside the pattern: /foo(?!bar)/. In the RegExp constructor, escape backslashes: new RegExp('foo(?!bar)'). Lookahead syntax has been supported since early JavaScript (ES3).
Lookahead is ideal when you need to match part of a string only if a specific suffix is absent—without including that suffix in the match. Alternation like /foo|foobaz/ lists allowed forms; (?!...) expresses a rule: match foo unless bar follows.
Negative lookahead is core JavaScript regex syntax since ES3. It works in every modern browser and Node.js version without polyfills.
Did you know?

Lookahead assertions were popularized by Perl and adopted into JavaScript early on. Because they never consume input, you can stack several at one position—for example, a password rule that uses multiple (?=...) and (?!...) checks before matching the actual characters.

Continue to + one-or-more quantifier

Learn how the + quantifier repeats the previous token one or more times.

+ quantifier tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful