Fix React Warning: Each Child in a List Should Have a Unique "key" Prop

beginnerโš›๏ธ React2026-03-26| React 16+, any OS, browser DevTools console

Error Message

Warning: Each child in a list should have a unique "key" prop.
#react#key#list#map

The Error

You open the browser console and see this warning:

Warning: Each child in a list should have a unique "key" prop.

Check the render method of `UserList`. See https://reactjs.org/link/warning-keys for more information.
    at li
    at UserList

React throws this when you map over an array and forget the key prop โ€” or when the keys you're using aren't actually unique across the list.

Why This Matters

React relies on key to track which items changed, moved, or were removed between renders. Without unique keys, its diffing algorithm has to guess โ€” and it guesses wrong.

Here's a concrete scenario: you have a todo list with 5 items. You delete item #2. With index keys, item #3 now occupies index 1. React thinks it's the same component as the old item #2 and skips updating it. The result? Checkboxes ticked on the wrong items, or form inputs showing stale values.

Specifically, bad keys cause:

  • Wrong component state after reorder or deletion
  • Unexpected re-renders or missed updates
  • Input fields retaining old values after list mutation

It's a warning, not a crash. Ignore it long enough, though, and you'll spend hours tracking down bugs that seem impossible to reproduce.

Common Causes

  • Missing key prop entirely on list items
  • Using array index as key when the list can be reordered or filtered
  • Duplicate IDs in the data
  • Key placed on the wrong element โ€” on a wrapper fragment instead of the outermost returned element

Step-by-Step Fix

1. Add a key to every list item

The most basic case โ€” you're mapping over an array without a key at all:

// โŒ Missing key
function UserList({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li>{user.name}</li>  // Warning fires here
      ))}
    </ul>
  );
}
// โœ… Add key using a stable unique ID
function UserList({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

2. Stop using array index as key

Index keys look harmless. They silence the warning. The trouble starts the moment a user sorts, filters, or deletes something:

// โŒ Index key โ€” breaks on reorder/delete
{items.map((item, index) => (
  <TodoItem key={index} item={item} />
))}
// โœ… Use a stable ID from your data
{items.map(item => (
  <TodoItem key={item.id} item={item} />
))}

Index keys are only safe for truly static lists โ€” things that never reorder, filter, or shrink. Everything else needs a real ID.

Generate IDs when the data is created (e.g., with crypto.randomUUID() or nanoid()), not during render. More on that in step 5.

3. Key on fragments

Returning multiple elements per item? You need a Fragment with a key. The shorthand <></> syntax doesn't accept props, so you must use the explicit <Fragment> import:

import { Fragment } from 'react';

// โŒ Shorthand fragment can't receive key
{items.map(item => (
  <>
    <dt>{item.term}</dt>
    <dd>{item.definition}</dd>
  </>
))}

// โœ… Explicit Fragment with key
{items.map(item => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.definition}</dd>
  </Fragment>
))}

4. Nested lists

Every level of a list needs keys โ€” not just the outermost one:

// โŒ Inner list items missing keys
{categories.map(cat => (
  <div key={cat.id}>
    <h3>{cat.name}</h3>
    <ul>
      {cat.items.map(item => (
        <li>{item.label}</li>  // โ† Warning here too
      ))}
    </ul>
  </div>
))}

// โœ… Keys on every mapped element
{categories.map(cat => (
  <div key={cat.id}>
    <h3>{cat.name}</h3>
    <ul>
      {cat.items.map(item => (
        <li key={item.id}>{item.label}</li>
      ))}
    </ul>
  </div>
))}

5. When you genuinely have no ID

Some lists are truly static โ€” a fixed set of language names, status labels, or config options that never reorder and never repeat. In that case, the value itself is a valid key:

const SUPPORTED_LANGS = ['JavaScript', 'TypeScript', 'Python', 'Go'];

{SUPPORTED_LANGS.map(lang => (
  <li key={lang}>{lang}</li>
))}

Dynamic data is a different story. Attach an ID at the source โ€” before the data ever reaches a component. A quick way with nanoid:

import { nanoid } from 'nanoid';

const items = rawData.map(item => ({ ...item, id: nanoid() }));
// Now use item.id as key

Do this once, outside the component. Never call nanoid() or Math.random() inside a .map() during render โ€” each render generates a new key, which forces React to unmount and remount every item.

Verify the Fix

  • Open Chrome DevTools โ†’ Console tab
  • Reload the page โ€” the key warning should be gone
  • Stress-test the list: add items, delete them, reorder if possible
  • Check that input fields inside list items keep their values correctly after each operation
  • In React DevTools (Components tab), inspect list items โ€” keys are internal and won't show as props, but the console warning stays silent

Quick Checklist

  • Every .map() callback returns an element with a key prop
  • Keys come from stable data IDs, not array indices (unless the list is truly static and never mutates)
  • Keys are unique among siblings โ€” they don't need to be globally unique
  • Fragments with keys use <Fragment key={...}>, not <></>
  • Nested lists have keys at every level
  • Keys are never generated during render โ€” no Math.random(), no Date.now(), no inline nanoid() inside .map()

Related Error Notes