jsguides

String.prototype.includes()

includes(searchString[, position])

The includes() method determines whether one string can be found within another. It returns true if the substring exists anywhere in the string, and false if not. This method is case-sensitive and was introduced in ES6 as a more readable alternative to checking indexOf() !== -1.

One notable behavior: includes() returns true when searching for an empty string, since every string contains the empty string at every position.

Syntax

str.includes(searchString);
str.includes(searchString, position);

The method accepts a search string and an optional starting position for partial-string scans. TypeScript users can lean on the signature below for type-checking, though the runtime behaviour is the same whether or not you declare the types explicitly.

TypeScript Signature

interface String {
  includes(searchString: string, position?: number): boolean;
}

Parameters

ParameterTypeDefaultDescription
searchStringstringThe substring to search for. Cannot be a regex. All non-regex values are coerced to strings.
positionnumber0The index at which to begin searching. Defaults to 0.

Examples

Basic usage

const message = "Hello, world!";

console.log(message.includes("Hello"));
// true

console.log(message.includes("hello"));
// false (case-sensitive)

console.log(message.includes("world"));
// true

console.log(message.includes("JavaScript"));
// false

// Empty string always returns true
console.log(message.includes(""));
// true

The basic example covers exact-match checks and confirms that the search is case-sensitive. The position parameter gives you more control by limiting the search to a slice of the string, which is useful when you have already scanned an earlier portion and only care about what comes after.

Using the position parameter

const text = "The quick brown fox jumps over the lazy dog";

console.log(text.includes("fox", 10));
// true — search starts at index 10

console.log(text.includes("fox", 20));
// false — "fox" is at index 16, which is before position 20

// Searching from the beginning explicitly
console.log(text.includes("The", 0));
// true

With position, you can scan a string in stages without slicing it first, skipping earlier content that you have already processed. The examples below show how the same method applies to real-world checks like validating email fields, filtering spam words, and running case-insensitive searches.

Practical examples

// Validating user input
const email = "user@example.com";
const hasAtSymbol = email.includes("@");
console.log(hasAtSymbol);
// true

// Checking for forbidden words
const comment = "This is a great tutorial!";
const hasSpam = comment.includes("buy now") || comment.includes("click here");
console.log(hasSpam);
// false

// Case-insensitive search
const searchIn = "JavaScript Fundamentals";
const searchFor = "javascript";
const found = searchIn.toLowerCase().includes(searchFor.toLowerCase());
console.log(found);
// true

These one-off checks are common in validation logic, where you need a quick boolean answer rather than an index into the string. The patterns below show how includes replaces older idioms and chains with other string methods for more expressive conditions.

Common Patterns

Replacing indexOf() checks

// Old way
if (str.indexOf("error") !== -1) {
  console.log("Found error");
}

// Modern way
if (str.includes("error")) {
  console.log("Found error");
}

Replacing indexOf() !== -1 with includes() makes the intent clearer: you are checking presence, not hunting for a position. Once you have that boolean, you can chain it with other methods like startsWith and endsWith to build compound conditions that read like a sentence.

Chaining with other string methods

const url = "https://example.com/api/users";
const isSecure = url.startsWith("https") && url.includes("example");
console.log(isSecure);
// true

Chaining gives you readable compound checks, but the method still has edge cases you should know about — especially around type coercion and empty strings. The error-handling section below walks through those common surprises so you can avoid them in your own code.

Error Handling

Unlike some string methods, includes() does not throw an error when given undefined or no argument. Instead, it converts the value to a string:

console.log("undefined".includes(undefined));
// true — searches for the string "undefined"

console.log("hello".includes());
// false — searches for the string "undefined"

The coercion behaviour can mask bugs, so it is worth knowing before you ship validation code. A regex, however, will cause a hard error because includes is designed for plain substring checks, not pattern matching.

However, passing a regex throws a TypeError:

try {
  "test".includes(/test/);
} catch (e) {
  console.log(e.name);
  // TypeError
}

includes() vs indexOf()

includes() is the clearer choice when you only need to know whether a substring exists. It returns a boolean, which makes it a natural fit for if statements, validation rules, and feature checks. indexOf() is better when you also need the position of the match, because it gives you a number you can reuse for slicing or looping.

The methods also signal different intent to readers. includes() says “does this text contain that text?” while indexOf() says “where is the first occurrence?” That small difference can make parsing code much easier to understand at a glance.

Search behavior gotchas

An empty search string returns true, because every string contains the empty string. That is usually correct, but it can surprise code that expects a real token. Regex values are rejected because includes() is meant for plain substring checks, not pattern matching. If you need rules or wildcards, switch to match() or search().

The position argument is another detail worth remembering. It does not trim the string or reset the search term; it simply starts scanning later. That makes it ideal for staged parsing, but it also means a late position can hide earlier matches that you might still care about.

When to Use

Use includes() when you need to:

  • Check for substring presence with a simple boolean result
  • Validate user input (email format, forbidden words)
  • Make code more readable compared to indexOf() !== -1

When not to use

Avoid includes() when:

  • You need the position of the match — use indexOf() instead
  • You need case-insensitive matching — convert both strings to lowercase first
  • You need pattern matching — use match() or search() with regex

See Also