includes()

ES7+

Determines whether an array includes a certain value among its entries.

Syntax

array.includes(searchElement, fromIndex)

Parameters

searchElement any

The value to search for

fromIndex number optional

Position to start searching from

Return Value

boolean

true if the value is found, otherwise false

Examples

JavaScript
const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits.includes('banana'));
console.log(fruits.includes('mango'));
Output:
// true false

📌 When to Use

Use includes() for the simplest possible membership question: "is this exact primitive value in the array?" Checking whether a status is one of several allowed values - ["active", "trial"].includes(user.status) - is cleaner than a chain of === comparisons and scales to more values without new operators. It is also the standard tool for feature-flag lists, allow/deny lists, and validating that user input belongs to a fixed set of options. Three boundaries determine when to move to something else. First, includes() compares with the SameValueZero algorithm, essentially strict equality plus NaN equals NaN, so it can never match objects by content - two structurally identical objects are different references, which is what some() with a custom comparison handles. Second, includes() only answers yes or no; needing the position means indexOf(), and needing the element that matches a condition means find(). Third, membership testing that repeats many times against the same list belongs in a Set, whose has() is O(1) instead of a linear scan. Versus indexOf() !== -1, includes() is strictly better for readability and additionally handles NaN correctly, so in ES2016+ codebases there is no reason to prefer the indexOf() idiom for existence checks.

⚠️ Common Mistakes

Expecting includes() to match objects by content. It compares references, so cart.includes({id: 1}) is always false - the literal creates a brand-new object that cannot be reference-equal to anything already in the array. This "works with numbers, fails with objects" asymmetry trips up beginners constantly; use some(item => item.id === 1) for structural matching.

Assuming includes() and indexOf() !== -1 are interchangeable. They differ on exactly one value: [NaN].includes(NaN) is true while [NaN].indexOf(NaN) is -1, because indexOf() uses strict equality (NaN === NaN is false) and includes() uses SameValueZero. Data pipelines where failed parseFloat() calls leave NaN behind hit this discrepancy for real.

Carrying the legacy indexOf() !== -1 idiom into modern code. It still works, but it expresses a position query and then discards the position, adding a comparison the reader must decode. includes() has been safe to use everywhere since ES2016; the only remaining reason to see the old idiom is uninspected pre-2016 habits.

Overlooking type mismatches between the array and the probe value. [1, 2, 3].includes("2") is false because SameValueZero never coerces types, and ids from URLs or input fields are strings. Normalize with Number() or String() on one side before testing, or the check fails only in the code paths where the value came from user input.

Calling includes() inside a loop against the same big array. Each call rescans from the start, so filtering one list by membership in another this way costs O(n*m). Hoist a const lookup = new Set(bigArray) above the loop and test with lookup.has(x) instead.

✅ Best Practices

Replace multi-way equality chains with a literal-array test: if (["png", "jpg", "webp"].includes(ext)) reads as the allow-list it is, and adding a format touches one token. V8 handles the small temporary array cheaply, and hoisting it to a module constant removes even that cost from hot paths.

Match the comparison to the data: includes() for primitives, some(x => x.id === id) for objects, and a Set of extracted keys - new Set(items.map(i => i.id)) - when object membership must be tested repeatedly. Each step up trades a little setup for the right equality semantics and better complexity.

Build the Set once and reuse it: constructing new Set(arr) costs a full O(n) pass, so new Set(arr).has(val) inline is slower than arr.includes(val) for a single check. The Set pays off from the second lookup onward - hoist it out of loops and render functions.

In TypeScript, includes() on a readonly tuple of literals doubles as a runtime validator for union types: if (VALID_STATUSES.includes(input)) narrows cleanly with a small helper, giving one source of truth for both the type and the runtime check.

Use the fromIndex parameter to search only a suffix of the array - arr.includes(x, i + 1) checks for a duplicate after the current position without slicing a copy. Negative values count back from the end, mirroring slice() semantics.

⚡ Performance Notes

includes() performs a linear scan with SameValueZero comparison and stops at the first hit: O(n) worst case, no allocation, and no callback overhead - which makes it faster in practice than some() for plain equality since the comparison happens in native code rather than a JavaScript function invoked per element. For the tiny constant arrays typical of allow-lists (three to ten entries), it is effectively free and V8 optimizes such patterns heavily; do not convert those to Sets in the name of big-O. The crossover where a Set genuinely wins is repeated membership testing against a list of hundreds of elements or more - a filter of 10,000 rows against a 1,000-entry blocklist does 10 million comparisons with includes() but 10,000 hashed lookups with Set.has(). One extra scan-cost subtlety: a miss always costs the full array walk, so workloads dominated by misses (spam checks, dedup guards) feel the O(n) earlier than workloads dominated by early hits.

🌍 Real World Example

Checking User Roles for Access Control

Access control code reads best when it mirrors the sentence in the requirements document, and includes() gets it closest: "show the panel if the enabled features include dark-mode". The example demonstrates the two directions this pattern runs in practice - checking whether any of the privileged roles appears in the user's role list (some() + includes() across two arrays), and the simpler single-value feature-flag test. Both checks are pure reads with no allocation beyond the literal array, so they are safe to call inside render functions. If the role lists came from a database with hundreds of entries, the inner includes() would be the part to replace with a Set.

const userRoles = ['user', 'editor'];
const adminRoles = ['admin', 'superadmin'];

const isAdmin = adminRoles.some(role => userRoles.includes(role));
// isAdmin: false

// Simple feature flag check
const enabledFeatures = ['dark-mode', 'notifications', 'analytics'];
if (enabledFeatures.includes('dark-mode')) {
  enableDarkMode();
}

Related Methods