String.prototype.includes()

Exists purely for readability. It answers the question you actually asked, instead of making you compare an index against -1 and hope you got the operator right.

String methodES2015Live demo
Common call
s.includes('abc')
Returns
boolean
Replaces
s.indexOf('abc') !== -1
Watch out
case sensitive, and it rejects a RegExp argument
string.includes(searchString[, position])
→ boolean

Demo

Live evaluation
Try:
Inputs
sstringthe string to search
searchstringtext to look for
Output
'hello world'.includes('world')
true

A match anywhere counts, including in the middle of a word — 'ell' is found inside 'hello', which matters when testing against user input where a word boundary was intended. The comparison is case sensitive and does no normalisation whatsoever, so 'HELLO' is simply absent from 'hello'. The last case surprises people: an empty string is considered present in every string, including the empty string itself, because it trivially occurs at position 0.

Parameters

NameTypeRequiredDescription
searchStringstringyesThe text to look for. A RegExp here is a TypeError — this method is for literal text only.
positionnumberno (0)Index to start searching from. Anything before it is ignored entirely.

Return value

boolean — True if the search string occurs anywhere at or after position. Always a boolean — never an index.

Common patterns

Case-insensitive test
Lowercase both sides; there is no flag for this.
const found = s.toLowerCase().includes(needle.toLowerCase());
Filter a list
The everyday search-box implementation.
const hits = items.filter(i => i.name.toLowerCase().includes(term));
Match any of several
some reads better than a chain of ORs.
const isImage = EXTS.some(e => name.endsWith(e));

Examples

1. Present
'hello'.includes('ell')
Returns
true
2. Absent
'hello'.includes('xyz')
Returns
false
3. Case sensitive
'hello'.includes('L')
Returns
false
4. Empty is always true
'abc'.includes('')
Returns
true
5. From a position
'hello'.includes('h', 1)
Returns
false
6. RegExp throws
'abc'.includes(/b/)
Returns
TypeError: First argument to String.prototype.includes must not be a regular expression

Pitfalls

1. It is case sensitive with no option to change that
There is no flag and no locale-aware variant. Every case-insensitive search has to lowercase both operands, which is also where accented text quietly goes wrong — lowercasing does not strip diacritics.
Misses it
'Hello'.includes('hello')
false
Fold both sides
'Hello'.toLowerCase().includes('hello')
true
2. An empty search string is always true
Every string contains the empty string, so a search box that has just been cleared reports a match against everything. Usually harmless in a filter, actively wrong when the result gates an action.
Always true
''.includes('')
true
Guard it
const found = needle !== "" && s.includes(needle);
false
3. A RegExp argument throws
Deliberate — the method is defined for literal text, and silently stringifying a regex would produce nonsense like "/b/". Use search or test when you need a pattern.
Throws
'abc'.includes(/b/)
TypeError: First argument to String.prototype.includes must not be a regular expression
Use test
/b/.test('abc')
true
4. A substring match is not a word match
Checking a role, a tag or a comma-joined list with includes matches fragments too — "admin" is found inside "non-admin". Split the list or use a word-boundary regex.
False positive
'non-admin'.includes('admin')
true
Compare items
roles.split(',').includes('admin')
false

When to use

Use it
  • Testing whether text contains a fragment
  • Search-box filtering, with both sides lowercased
  • Anywhere indexOf was only ever compared against -1
Reach for something else
  • You need the position → indexOf
  • You need a pattern → RegExp.test or search
  • You are checking the start or end → startsWith, endsWith
  • You are checking membership in a list → split then Array.includes

Notes

Complexity
O(n × m) worst case, though engines use faster substring searches in practice
Return
A boolean; nothing is allocated or modified
CPython impl
V8: Builtins-string-includes
Memory
No allocation
Thread-safe
Single-threaded; the string is only read

FAQ

Lowercase both operands. For text with accents that is still not enough — normalising and stripping combining marks handles é versus e, and localeCompare with sensitivity options is the thorough answer.

s.toLowerCase().includes(t.toLowerCase());
// accent-insensitive:
s.normalize('NFD').replace(/\p{Diacritic}/gu, '')

History

ES2015
includes added alongside startsWith and endsWith, replacing the indexOf !== -1 idiom.