The React reconciliation algorithm failed because it couldn’t efficiently track and update individual elements within a list, leading to potential performance issues and bugs.

Here’s why this happens and how to fix it:

Cause 1: Missing key Prop Entirely

Diagnosis: When you render a list of elements in React without a key prop on the child elements, React has no way to distinguish between them when the list changes. It will often log a warning directly in the browser console: "Warning: Each child in a list should have a unique 'key' prop."

Fix: Add a key prop to each element rendered within your .map() function. The key should be a stable, unique identifier for that specific item.

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

Why it works: The key prop provides React with a stable identity for each element. When the list updates (items are added, removed, or reordered), React uses these keys to quickly determine which elements have changed, been added, or been removed, rather than re-rendering the entire list from scratch. This is crucial for performance.

Cause 2: Using Array Index as a key

Diagnosis: You might have implemented keys using the index of the array element. This often looks like:

function MyListComponent({ items }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

While this satisfies the initial warning, it’s problematic if the list can be reordered, items can be added in the middle, or items can be deleted from the middle.

Fix: If your list items have a stable, unique ID (e.g., item.id), use that as the key.

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

Why it works: Using a stable ID ensures that React correctly tracks the same item even if its position in the array changes. If you use the index, and an item is inserted at index 0, all subsequent items’ indices shift, and React might incorrectly think they are new items or that existing items have been deleted and re-added, leading to unnecessary re-renders or incorrect component state.

Cause 3: Non-Unique key Prop Values

Diagnosis: You might have a key prop, but the values are not unique across the entire list at any given time. This often happens when the "unique" value isn’t actually unique, or if you’re generating keys without proper de-duplication. React will warn: "Warning: Each child in a list should have a unique 'key' prop. See https://reactjs.org/link/warning-keys for more information."

Fix: Ensure that the value you assign to key is guaranteed to be unique for every item currently in the list. If your data doesn’t provide a suitable ID, you might need to generate one, but be cautious.

// Example if item.id isn't reliable, but you have other unique properties
function MyListComponent({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={`${item.type}-${item.timestamp}-${item.userId}`}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

If you truly have no other unique identifier, and the list is static (never reordered, items not added/deleted in the middle), using the index might be acceptable, but it’s generally discouraged.

Why it works: Uniqueness is the fundamental requirement for keys. React uses them as a hash to identify specific DOM nodes. If keys are duplicated, React cannot reliably map a key to a specific element, leading to unpredictable behavior.

Cause 4: Keys on Components, Not Elements

Diagnosis: You might be applying the key prop to a custom component that wraps the list item, but the key is not being passed down or applied to the actual DOM element that React needs to track.

// Incorrect
function ListItem({ item }) {
  return <li key={item.id}>{item.name}</li>; // Key is on the <li> inside ListItem
}

function MyListComponent({ items }) {
  return (
    <ul>
      {items.map(item => (
        <ListItem key={item.id} item={item} /> // Key is on the ListItem component itself
      ))}
    </ul>
  );
}

React warns about keys on the ListItem component, not the li element it renders.

Fix: The key prop should always be placed directly on the element or component that is part of the list being mapped. In most cases, this means the key should be on the direct child of the .map() return statement.

// Correct
function ListItem({ item }) {
  // The key belongs on the element returned by the map function, not inside the component
  return <li>{item.name}</li>;
}

function MyListComponent({ items }) {
  return (
    <ul>
      {items.map(item => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
}

Or, if ListItem is just a wrapper and you want to apply the key to the li:

function MyListComponent({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          <ListItem item={item} />
        </li>
      ))}
    </ul>
  );
}

Why it works: React’s reconciliation algorithm operates on the direct children returned by the map function. The key prop is a special prop that React uses internally for identification and is not passed down to the underlying DOM element or other component props by default. It needs to be on the component instance that React is managing in the list.

Cause 5: Keys on Fragments

Diagnosis: When using React Fragments (<></> or <React.Fragment>) to group multiple elements in a list, you must also add the key prop to the Fragment itself.

// Incorrect
function MyListComponent({ items }) {
  return (
    <>
      {items.map(item => (
        <>
          <span key={item.id}>{item.name}</span>
          <hr />
        </>
      ))}
    </>
  );
}

The key is on the span, but the hr is also part of the list item’s rendered output, and the fragment containing both needs a key.

Fix: Apply the key prop directly to the React.Fragment or use the shorthand <></> with a key attribute.

// Correct
function MyListComponent({ items }) {
  return (
    <>
      {items.map(item => (
        <React.Fragment key={item.id}>
          <span>{item.name}</span>
          <hr />
        </React.Fragment>
      ))}
    </>
  );
}

Why it works: When a fragment is part of a list, React needs a key to identify the group of elements as a single logical unit. Without it, React doesn’t know how to track the fragment’s children efficiently when they change.

After fixing your keys, you might encounter a warning about "Too many re-renders" if your state updates are causing infinite loops, or issues related to context providers if your list items rely on shared context that isn’t being updated correctly.

Want structured learning?

Take the full React course →