logoESLint React

no-array-index-key

Disallows using an item's index in the array as its key.

Full Name in eslint-plugin-react-x

react-x/no-array-index-key

Full Name in @eslint-react/eslint-plugin

@eslint-react/no-array-index-key

Presets

x recommended recommended-typescript recommended-type-checked strict strict-typescript strict-type-checked

Rule Details

The order of items in list rendering can change over time if an item is inserted, deleted, or the array is reordered. Using indexes as keys often leads to subtle, confusing errors.

Examples

Rendering a list with stable identities

When your data items have a unique identifier, use it as the key so React can correctly track each element across reorders, insertions, and deletions.

// Problem: Using the array index as a key causes state issues when sorting, adding, or removing items
interface MyComponentProps {
  items: { id: string; name: string }[];
}

function MyComponent({ items }: MyComponentProps) {
  return (
    <ul>
      {items.map((item, index) => (
        //              ^^^ Do not use an item's index in the array as its key.
        <li key={index}>{item.name}</li>
      ))}
    </ul>
  );
}
// Recommended: Use the data's own unique identifier as the key
interface MyComponentProps {
  items: { id: string; name: string }[];
}

function MyComponent({ items }: MyComponentProps) {
  return (
    <ul>
      {items.map((item) => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

Deriving the key from the index

Wrapping the index in a string or a conversion does not make it a stable identity — the key still changes whenever the list is reordered. The rule also reports indexes used in template literals, string concatenation, and String(), Number(), or .toString() conversions.

// Problem: The key is still derived from the array index
interface MyComponentProps {
  items: { id: string; name: string }[];
}

function MyComponent({ items }: MyComponentProps) {
  return (
    <ul>
      {items.map((item, index) => (
        //              ^^^ Do not use an item's index in the array as its key.
        <li key={`item-${index}`}>{item.name}</li>
      ))}
      {items.map((item, index) => (
        //              ^^^ Do not use an item's index in the array as its key.
        <li key={String(index)}>{item.name}</li>
      ))}
    </ul>
  );
}
// Recommended: Derive the key from the data itself
interface MyComponentProps {
  items: { id: string; name: string }[];
}

function MyComponent({ items }: MyComponentProps) {
  return (
    <ul>
      {items.map((item) => <li key={`item-${item.id}`}>{item.name}</li>)}
    </ul>
  );
}

Falling back to the index

Using the index as a fallback when an identifier is missing still gives some items an unstable identity. The rule checks every branch a key may evaluate to, including ??, ||, and ternary expressions.

// Problem: Items without an id silently fall back to the index
interface MyComponentProps {
  items: { id?: string; name: string }[];
}

function MyComponent({ items }: MyComponentProps) {
  return (
    <ul>
      {items.map((item, index) => (
        //              ^^^ Do not use an item's index in the array as its key.
        <li key={item.id ?? index}>{item.name}</li>
      ))}
    </ul>
  );
}
// Recommended: Assign a stable id when the item is created, not at render time
interface MyComponentProps {
  items: { id: string; name: string }[];
}

function createItem(name: string) {
  return { id: crypto.randomUUID(), name };
}

function MyComponent({ items }: MyComponentProps) {
  return (
    <ul>
      {items.map((item) => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

Rendering nested lists

When rendering nested lists, both the outer and inner items need stable keys. Using the item's own identifier ensures React can correctly track elements at every level.

// Recommended: Use stable identifiers for both outer and inner list items
interface Recipe {
  id: string;
  name: string;
  ingredients: string[];
}

function RecipeList({ recipes }: { recipes: Recipe[] }) {
  return (
    <div>
      <h1>Recipes</h1>
      {recipes.map((recipe) => (
        <div key={recipe.id}>
          <h2>{recipe.name}</h2>
          <ul>
            {recipe.ingredients.map((ingredient) => (
              <li key={ingredient}>{ingredient}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

Rendering multiple elements per list item

When each list item needs to render multiple elements, use Fragment with a stable key so React can track the group without adding extra DOM nodes.

// Recommended: Use Fragment with a stable key for multi-element items
import { Fragment } from "react";

interface Person {
  id: string;
  name: string;
  bio: string;
}

function List({ people }: { people: Person[] }) {
  const listItems = people.map((person) => (
    <Fragment key={person.id}>
      <h1>{person.name}</h1>
      <p>{person.bio}</p>
    </Fragment>
  ));
  return <ul>{listItems}</ul>;
}

Versions

Resources

Further Reading


See Also

On this page