The Problem

React uses the key prop to track which items in a list have changed, been added, or been removed. When keys are duplicated, missing, or unstable, React cannot efficiently update the DOM and may render incorrect content.

Symptoms

  • Console warning: "Encountered two children with the same key"
  • List items display wrong content after filtering or sorting
  • Input fields retain old values when items are reordered
  • Animations glitch on list updates
  • Checkbox state persists on wrong items after filtering

Real Error Message

bash
Warning: Encountered two children with the same key, `item-3`. Keys
should be unique so that components maintain their identity across updates.
Non-unique keys may cause children to be duplicated and/or omitted.
    at li
    at TodoList

Common Causes

Cause 1: Using Array Index as Key

javascript
// BAD: Index changes meaning when items are reordered or filtered
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>{todo.text}</li>  // Index 0 could be any item after filter
      ))}
    </ul>
  );
}

If you filter out the first todo, the second todo now has key 0 but still contains the same DOM state (like a checked checkbox). React thinks it's the first item.

Cause 2: Non-Unique IDs from Backend

javascript
// BAD: Backend returns duplicate IDs
function UserList({ users }) {
  return users.map(user => (
    <div key={user.id}>{user.name}</div>  // Two users share id=42
  ));
}

Cause 3: Generated Keys from Non-Unique Data

javascript
// BAD: Multiple items can have the same category
function ProductGrid({ products }) {
  return products.map(product => (
    <Card key={product.category}>{product.name}</Card>  // Many "Electronics"
  ));
}

How to Fix It

Fix 1: Use Stable Unique IDs

javascript
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>  // UUID from database
      ))}
    </ul>
  );
}

Fix 2: Generate Unique Keys for Backend Data Without IDs

javascript
function UserList({ users }) {
  return users.map((user, index) => (
    <div key={`user-${user.name}-${index}`}>{user.name}</div>
  ));
}

Fix 3: Deduplicate Data Before Rendering

```javascript function ProductGrid({ products }) { const unique = useMemo(() => { const seen = new Set(); return products.filter(p => { if (seen.has(p.id)) return false; seen.add(p.id); return true; }); }, [products]);

return unique.map(product => ( <Card key={product.id}>{product.name}</Card> )); } ```

Fix 4: Use crypto.randomUUID for Client-Generated Items

javascript
function TodoApp() {
  const addTodo = (text) => {
    const newTodo = {
      id: crypto.randomUUID(),  // Guaranteed unique
      text,
      completed: false
    };
    setTodos(prev => [...prev, newTodo]);
  };
}

Prevention

  • Never use array index as key when items can be reordered, filtered, or deleted
  • Always use database-generated IDs or UUIDs
  • Run eslint-plugin-react with the react/jsx-key rule enabled
  • Test list components with filter, sort, and reorder scenarios