Table of Contents

The JavaScript Array findIndex() method searches an array with a callback and returns the index of the first element that meets a condition. It returns -1 if nothing matches. This makes it useful when you need the position of an object or value before updating, removing, or replacing it. The examples below build on the existing syntax and callback details with practical patterns for checking results and searching a selected range.

Syntax and Formal Specification

The method signature is findIndex(callbackFn, thisArg).

javascript
const numbers = [5, 12, 8, 130, 44];
const firstBigIndex = numbers.findIndex(n => n > 10);
console.log(firstBigIndex);   // 1

Parameters

  • callbackFn — A function invoked for each element. It receives three arguments:

    • element — The current element being processed.

    • index — The integer index of the current element.

    • array — The array on which findIndex() was called.

  • thisArg (optional) — A value used as this inside the callback when the callback is a non-arrow function. Ignored for arrow functions, which lexically bind this.

Return Value

  • The integer index of the first element for which the callback returns a truthy value.

  • -1 if no element satisfies the predicate (including when the array is empty).

javascript
console.log([1, 2, 3].findIndex(x => x > 5));   // -1
console.log([1, 2, 3].findIndex(x => x > 1));   // 1
console.log([].findIndex(x => true));           // -1

Callback Arguments: Element, Index, Array

The predicate receives the same context that find() provides. That context is often what distinguishes findIndex() from simpler search methods—the index argument allows comparisons relative to array position:

javascript
const values = [10, 20, 30, 40];

// Find the index of the first element greater than the previous element
const idx = values.findIndex((value, index, arr) => {
  if (index === 0) return false;
  return value > arr[index - 1] && value > 25;
});
console.log(idx);   // 2

// Find the first index where the value equals (index + 1) * 10
const patternIdx = values.findIndex((v, i) => v === (i + 1) * 10);
console.log(patternIdx);   // 0

Immutability and Purity

findIndex() does not mutate the array. It returns an integer index, not a reference. No array element is returned, so there is no reference-aliasing concern. This makes findIndex() inherently safer than find() in code that might later mutate the returned value.

javascript
const records = [{ id: 1, hits: 0 }, { id: 2, hits: 5 }];
const recordIdx = records.findIndex(r => r.id === 2);
console.log(recordIdx);   // 1

// The index is a plain number; there is nothing to alias
records[recordIdx].hits += 1;
console.log(records[1].hits);   // 6 (intentional mutation via index)

Because findIndex() returns a primitive integer, the result is a completely detached value. Even if the array is later reassigned or mutated, the returned index remains valid (unless the mutation shifts elements). This is a subtle but important property in reactive frameworks and state management systems.

Note on Mutation Inside the Callback

Because findIndex() reads elements during iteration, mutating the array from inside the callback can produce unexpected results. findIndex() captures the array length before iteration. Items appended beyond that original length are not visited; changing or deleting a later existing element can affect the value the callback sees when that index is reached. Avoid mutating the array from inside a findIndex() predicate.

javascript
// Anti-pattern: do not mutate during findIndex()
const arr = [1, 2, 3, 4];
arr.findIndex(n => {
  if (n === 2) arr.push(99);   // pushed beyond the initial length, so it is not visited
  return n === 3;
});

Performance Characteristics

findIndex() runs in O(n) time worst case, but short-circuits at the first match, so its effective cost depends on where the first match appears. For arrays where the target appears near the start, findIndex() completes quickly; for arrays where no match exists, it scans every element.

javascript
const big = new Array(1_000_000).fill({ active: false });
big[0] = { active: true };

const t1 = performance.now();
big.findIndex(x => x.active);      // returns 0 immediately
console.log(performance.now() - t1);   // near-instant

const t2 = performance.now();
big.findIndex(x => x.active && x === null);   // no match
console.log(performance.now() - t2);          // full scan

findIndex() vs find() + indexOf()

A naive alternative is arr.indexOf(arr.find(predicate)). This calls find() (full scan until match), then indexOf() (another linear scan to locate the matched reference). The result is two passes where findIndex() needs only one. The findIndex() approach is also correct when the match is undefined (which indexOf treats as a real value, not a “no match” sentinel).

javascript
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Cara" }
];

// Wasteful: two scans
const wasteful = users.indexOf(users.find(u => u.name === "Bob"));

// Idiomatic: single scan
const idiomatic = users.findIndex(u => u.name === "Bob");

console.log(wasteful, idiomatic);   // 1 1

findIndex() vs indexOf()

indexOf() matches using strict equality against a fixed value, so it does not match NaN. findIndex() accepts an arbitrary predicate. indexOf() is faster per iteration because it avoids a function call; findIndex() is more flexible. Use indexOf() for exact primitive matches, findIndex() for structural or compound predicates.

javascript
const ids = [1, 2, 3];
console.log(ids.indexOf(2));                 // 1 (fast, strict equality)
console.log(ids.findIndex(x => x === 2));    // 1 (flexible, function call)

findIndex() with Async Predicates

findIndex() does not support async predicates. The callback returns a Promise, which is always truthy, so the first element is always matched. Use a manual async loop instead:

javascript
async function findIndexAsync(arr, predicate) {
  for (let i = 0; i < arr.length; i++) {
    if (await predicate(arr[i], i, arr)) return i;
  }
  return -1;
}

// Usage:
// const idx = await findIndexAsync(items, async item => await checkAsync(item));

Use findIndex() to update or remove an item

A common use is locating an object by a unique property, such as an ID. Check that the result is not -1 before using it as an array position.

const tasks = [
  { id: 10, title: "Plan" },
  { id: 11, title: "Review" },
  { id: 12, title: "Publish" }
];

const taskIndex = tasks.findIndex(task => task.id === 11);

if (taskIndex !== -1) {
  tasks[taskIndex] = { ...tasks[taskIndex], title: "Approve" };
}

console.log(tasks[1].title); // "Approve"

The method itself only returns a number; it does not change the array. In this example, the assignment updates the list after the search. To remove the item instead, pass the verified index to splice(taskIndex, 1). If the ID is missing, the guard prevents -1 from being treated as the last array position.

Check index 0 and the not-found result

Array positions begin at 0, so a match at the first element produces the number 0. A missing match produces -1. These numbers are both truthy or falsy in ways that can trip up a conditional: 0 is falsy, while -1 is truthy. Compare explicitly with -1.

const values = ["ready", "pending"];
const readyIndex = values.findIndex(value => value === "ready");
const missingIndex = values.findIndex(value => value === "done");

console.log(readyIndex);   // 0
console.log(missingIndex); // -1

if (readyIndex !== -1) {
  console.log("The item exists");
}

A condition such as if (readyIndex) would incorrectly reject the first item. A condition such as if (missingIndex) would incorrectly treat a missing value as found.

Search within part of an array

findIndex() does not have a fromIndex argument; its search begins at index 0. To search from a later position, slice the array from that offset, find the relative index, then convert it back to the original array’s index.

const scores = [10, 20, 30, 40, 50];
const startAt = 2;

const relativeIndex = scores
  .slice(startAt)
  .findIndex(score => score >= 40);

const actualIndex =
  relativeIndex === -1 ? -1 : startAt + relativeIndex;

console.log(actualIndex); // 3

Handle -1 before adding the offset; otherwise a missing result would become a different negative number. This approach creates a temporary shallow copy of the selected range. For a large array or a search that must avoid copying, use a simple loop starting at the desired index.