jsguides

String.prototype.lastIndexOf()

lastIndexOf(searchString, fromIndex?)

The lastIndexOf() method searches for the last occurrence of a substring within a string, starting from the end. It returns the index position where the substring starts, or -1 if the substring is not found. This method is the inverse of indexOf(), which searches from the beginning.

Syntax

string.lastIndexOf(searchString)
string.lastIndexOf(searchString, fromIndex)
  • searchString: The substring to search for. If omitted, lastIndexOf() searches for the string "undefined".
  • fromIndex (optional): The index to start searching backward from. Defaults to string.length - 1. If greater than or equal to the string length, the entire string is searched.
  • Returns: A number representing the index of the last occurrence, or -1 if not found.

TypeScript Signature

The method accepts a search string and an optional position to start scanning backward from. When fromIndex is omitted, the search begins at the last character. The return type is always number, representing either a valid index or -1 for a missing match.

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

Basic Examples

The simplest usage passes only the substring you want to locate. lastIndexOf() scans from the end and returns the position where the match begins, counting from zero as usual. If the search text appears multiple times, you get the index of the rightmost occurrence.

const text = "hello world hello";

console.log(text.lastIndexOf("hello"));  // 12 (last occurrence)
console.log(text.lastIndexOf("world"));  // 6
console.log(text.lastIndexOf("!"));      // -1 (not found)

Using the fromIndex Parameter

The fromIndex argument sets a ceiling for the search. The method scans backward starting at fromIndex rather than the end of the string, which lets you skip occurrences that appear later in the text. This is especially helpful when you want to find progressively earlier matches of the same substring by walking the index backward manually.

const text = "hello world hello";

console.log(text.lastIndexOf("hello"));        // 12 (searches entire string)
console.log(text.lastIndexOf("hello", 10));   // 0 (stops before index 10)

// Finding all occurrences manually
const str = "to be or not to be";
const firstBe = str.lastIndexOf("be");        // 16
const secondBe = str.lastIndexOf("be", 15);   // 3
console.log(firstBe, secondBe);               // 16, 3

Practical use case: file extension extraction

lastIndexOf() is ideal for extracting file extensions because you always want the dot after the final directory separator or filename segment. Searching backward ensures you find .tar.gz as gz rather than incorrectly splitting on the first dot. The method pairs naturally with slice() to carve out the extension substring in one clean line.

function getExtension(filename) {
  const dotIndex = filename.lastIndexOf(".");
  if (dotIndex === -1 || dotIndex === 0) {
    return ""; // No extension or hidden file
  }
  return filename.slice(dotIndex + 1);
}

console.log(getExtension("document.pdf"));      // "pdf"
console.log(getExtension("image.png"));         // "png"
console.log(getExtension("archive.tar.gz"));    // "gz" (last dot)
console.log(getExtension("README"));            // "" (no extension)

Finding the last separator in a path

Path parsing is another natural fit for lastIndexOf(). Whether you are working with Unix forward slashes or Windows backslashes, the final separator in a file path is almost always the one that matters. Calling lastIndexOf on both separator characters and taking the larger index gives you a cross-platform solution for extracting the filename from a full path.

function getBasename(filepath) {
  const lastSlash = Math.max(
    filepath.lastIndexOf("/"),
    filepath.lastIndexOf("\\")
  );
  return lastSlash === -1 ? filepath : filepath.slice(lastSlash + 1);
}

console.log(getBasename("/home/user/documents/file.txt")); // "file.txt"
console.log(getBasename("C:\\Users\\Admin\\data.csv"));    // "data.csv"

Case Sensitivity

lastIndexOf() is case sensitive, just like indexOf(). It treats "Java" and "java" as different substrings, so a mismatch in casing produces -1 even when the letters are otherwise identical. If you need case-insensitive searches, convert both the target string and the search term to the same case before calling the method — text.toLowerCase().lastIndexOf(term.toLowerCase()) is the standard approach.

const text = "JavaScript JavaScript";

console.log(text.lastIndexOf("Java"));    // 10
console.log(text.lastIndexOf("java"));    // -1 (lowercase doesn't match)
console.log(text.lastIndexOf("SCRIPT")); // -1 (uppercase doesn't match)

Edge Cases

Several inputs produce results that are worth testing before relying on them. Empty strings, negative fromIndex values, and oversized fromIndex values all behave in ways that are documented but can still surprise you if you have not seen them before. Running through these cases once will save debugging time later.

const text = "hello";

// Empty string returns fromIndex or string length
console.log(text.lastIndexOf(""));        // 5 (string length)
console.log(text.lastIndexOf("", 3));    // 3
console.log(text.lastIndexOf("", -1));   // 5 (treated as 0)

// fromIndex greater than string length
console.log(text.lastIndexOf("h", 100)); // 0 (searches entire string)

// Negative fromIndex
console.log(text.lastIndexOf("h", -5));  // -1 (searches nothing)

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

Why search backward

Searching from the end is handy when the last separator carries special meaning. File extensions, path segments, version tags, and suffixes all tend to live near the end of a string, so lastIndexOf() often gives you the exact slice point you need with one call. That makes it a natural partner for slice() and substring().

The method is also useful when the same token can appear many times but only the last one matters. Logs, CSV rows, and user-entered text often follow that pattern. Instead of scanning every match, you can jump directly to the final occurrence and then work backward from there.

Edge cases to keep in mind

An empty search string returns a position based on fromIndex, which can be surprising if you expected -1. Negative fromIndex values do not search backward from the start of the string; they effectively stop the search, which is why the result is often -1. When you need careful input handling, validate the substring first and make sure the search target is actually meaningful.

Another subtle point is case sensitivity. lastIndexOf() does not normalize text for you, so if user input may vary in case, convert both strings first or compare with a locale-aware approach. That small step avoids false negatives in user-facing search features and keeps behavior aligned with what people expect to find.

Key Differences from indexOf()

AspectindexOf()lastIndexOf()
Search directionForward (start → end)Backward (end → start)
Default fromIndex0string.length - 1
Use caseFirst occurrenceLast occurrence

Browser Compatibility

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

Universal support across all modern and legacy JavaScript environments.

See Also