jsguides

String.prototype.indexOf()

indexOf(searchString, fromIndex?)

The indexOf() method searches for the first occurrence of a substring within a string, starting from the beginning. It returns the index position where the substring starts, or -1 if the substring is not found. This is one of the most commonly used string methods in JavaScript for finding substrings.

Syntax

string.indexOf(searchString)
string.indexOf(searchString, fromIndex)
  • searchString: The substring to search for. If omitted, indexOf() returns 0 when searching for an empty string, or -1 otherwise.
  • fromIndex (optional): The index to start searching from. Defaults to 0. Negative values are treated as 0.
  • Returns: A number representing the index of the first occurrence, or -1 if not found.

TypeScript Signature

interface String {
  indexOf(searchString: string, fromIndex?: number): number;
}

The TypeScript signature formalizes the contract by specifying parameter types and return type. With those types established, the basic examples below show how indexOf() behaves with real strings—finding matches at the start, in the middle, and returning -1 when nothing matches.

Basic Examples

const text = "hello world";

console.log(text.indexOf("hello"));  // 0 (starts at index 0)
console.log(text.indexOf("world"));  // 6
console.log(text.indexOf("!"));      // -1 (not found)
console.log(text.indexOf("o"));      // 4 (first occurrence)

A single call to indexOf() finds the first match. When the same substring appears multiple times in the target string, looping with a shifting fromIndex collects every occurrence without missing any.

Finding multiple occurrences

To find all occurrences of a substring, you need a loop that advances the start position past each match. This pattern works for any substring length and handles overlapping matches correctly when you increment by one.

const text = "hello hello hello";
const search = "hello";
let positions = [];
let idx = text.indexOf(search);

while (idx !== -1) {
  positions.push(idx);
  idx = text.indexOf(search, idx + 1);
}

console.log(positions);  // [0, 6, 12]

Using the fromIndex Parameter

The second parameter controls where the search starts, letting you skip past characters at the beginning of the string. A fromIndex of zero searches the whole string, while a value beyond the match position causes indexOf() to skip it entirely.

const text = "hello world hello";

console.log(text.indexOf("hello"));        // 0 (searches from start)
console.log(text.indexOf("hello", 1));    // 6 (skips first occurrence)
console.log(text.indexOf("world", 10));  // -1 (starts past where world is)

Practical use case: checking for keywords

A common pattern is to check if a string contains a word or phrase by testing whether indexOf() returns a non-negative index. This pattern predates includes() but remains widely used in older codebases and when the exact match position matters for further processing.

function containsKeyword(text, keyword) {
  return text.indexOf(keyword) !== -1;
}

console.log(containsKeyword("JavaScript is great", "Script")); // true
console.log(containsKeyword("JavaScript is great", "Type"));    // false

Case Sensitivity

The indexOf() method is case sensitive, which means uppercase and lowercase forms of the same letter are treated as different characters. A search for "java" will not match "Java", and "SCRIPT" will not match "Script". This strict matching is by design—it keeps the behavior predictable and fast—but it means you need to decide early whether your search should fold case.

const text = "JavaScript JavaScript";

console.log(text.indexOf("Java"));    // 0
console.log(text.indexOf("java"));    // -1 (lowercase does not match)
console.log(text.indexOf("SCRIPT"));  // -1 (uppercase does not match)

// For case-insensitive search, convert to lowercase first
const lower = text.toLowerCase();
console.log(lower.indexOf("java"));   // 0

indexOf() matches characters exactly as written—uppercase and lowercase letters are distinct. This matters when searching user input or mixed-case data. The examples below show how the same search string can match or miss depending on casing, and the helper that follows wraps the pattern for reuse.

Case-insensitive search helper

function indexOfIgnoreCase(text, search) {
  return text.toLowerCase().indexOf(search.toLowerCase());
}

console.log(indexOfIgnoreCase("Hello WORLD", "hello")); // 0
console.log(indexOfIgnoreCase("Hello WORLD", "WORLD")); // 6

The case-insensitive helper above handles the common scenario by lowercasing both sides before comparing. The edge cases below cover less obvious inputs—empty strings, negative indices, and search strings longer than the target—that behave in ways worth knowing before they surprise you in production.

Edge Cases

const text = "hello";

// Empty string returns fromIndex (or 0)
console.log(text.indexOf(""));         // 0
console.log(text.indexOf("", 3));     // 3

// Negative fromIndex is treated as 0
console.log(text.indexOf("h", -5));    // 0

// fromIndex greater than string length
console.log(text.indexOf("h", 100));   // -1

// Search string longer than target
console.log(text.indexOf("hello world")); // -1

indexOf() vs includes()

When you only need to know whether a string contains a substring, includes() is the more readable choice. When you also need the position, indexOf() gives you the index directly. The table and example below compare the two.

MethodReturnsUse Case
indexOf()number (index or -1)When you need the position
includes()booleanWhen you only need to check existence
const text = "hello world";

console.log(text.indexOf("world"));   // 6 (found, returns position)
console.log(text.includes("world"));  // true (just checks existence)

Why indexOf() still matters

Even though includes() is often clearer when you only need a yes-or-no answer, indexOf() still matters because it gives you the exact position of the match. That position is useful when you want to slice the string, replace part of it, or keep searching for more matches after the first one. In other words, the index is often the real result.

The method is also a good building block for manual parsing. Many simple tokenizers, URL splitters, and filename helpers use indexOf() to locate a delimiter before extracting the left or right side. That style is easy to follow and avoids adding regex complexity when a plain substring search is enough.

Case sensitivity and search strategy

indexOf() compares characters exactly as written, so it will not fold case or normalize accents for you. If your input is user-facing text, decide early whether you want an exact byte-for-byte match or a friendlier comparison. Lowercasing both sides is often enough for ASCII text, but locale-sensitive content may need a more careful approach.

When you need every occurrence, a loop around indexOf() is usually the simplest pattern. That approach keeps the code explicit and lets you control the step size, which matters when matches can overlap or when you want to skip ahead after each hit.

Browser Compatibility

BrowserVersion
Chrome1+
Firefox1+
Safari1+
Edge12+
Node.js0.1.0+

Universal support across all modern and legacy JavaScript environments.

See Also