Blog
3 min read

"Each Child in a List Should Have a Unique Key Prop": What It Means

React's key warning appears when you render a list without telling React which item is which. Why keys matter, why array index is a bad key for lists that change, what to use instead, and how to fix the warning in fragments and nested maps.

Warning: Each child in a list should have a unique "key" prop.
Check the render method of `TodoList`.

This is React's most common warning. It's only a warning — the page still renders — but ignoring it leads to genuinely confusing bugs later.

What React is asking for

When you render a list:

{todos.map(todo => <TodoItem todo={todo} />)}

React needs to know which item is which between renders. If the list changes — an item is added at the top, one is deleted, the order changes — React uses the key to match old items to new ones, so it can update only what changed and keep each item's state attached to the right one.

Give each item a stable, unique key:

{todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}

What to use as a key

Best: an ID from your data. A database ID, a UUID, a slug — anything that uniquely identifies the item and doesn't change. (UUID vs auto-increment)

OK: something naturally unique. An email address in a list of users, a date in a calendar.

Risky: the array index.

{todos.map((todo, index) => <TodoItem key={index} todo={todo} />)}

This silences the warning, but it tells React "item 0 is whatever's first." If you delete the first item, every item's key shifts by one, and React thinks the last item was deleted and every other item changed. Any state inside the items — a half-typed input, a checkbox, an open dropdown — ends up attached to the wrong row.

Index is acceptable only when the list never reorders, filters or has items inserted — a static list of footer links, say.

Never: random values.

key={Math.random()}   // ❌

A new key every render means React throws away and rebuilds every item every time. Inputs lose focus, animations restart, performance drops.

Where the key goes

On the outermost element returned from map — not on something inside it:

// ❌ key is on the inner element
{users.map(user => (
  <div>
    <UserCard key={user.id} user={user} />
  </div>
))}

// ✅
{users.map(user => (
  <div key={user.id}>
    <UserCard user={user} />
  </div>
))}

Fragments need keys too

The short <>...</> syntax can't take a key. Use the long form:

import { Fragment } from 'react'

{items.map(item => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.definition}</dd>
  </Fragment>
))}

Keys only need to be unique among siblings

Two different lists can use the same IDs. Keys only have to be unique within one map. If your data has duplicate IDs in the same list — a sign of a data bug — you'll get a "two children with the same key" warning instead.

Your data has no IDs

Generate them when the data is created, not when it's rendered:

const newTodo = { id: crypto.randomUUID(), text }
setTodos([...todos, newTodo])

Keys as a reset button

Changing a component's key makes React treat it as a brand-new component, resetting all its state. It's a handy trick: <ProfileForm key={user.id} user={user} /> resets the form when you switch users. (useState explained)

The summary

  • Keys tell React which list item is which between renders.
  • Use a stable ID from your data.
  • Index keys break state when lists reorder; random keys break everything.
  • Put the key on the outermost element in the map; use <Fragment key> for fragments.

EasySpawn runs your app and Claude Code on the same persistent server, so you can ask it to clean up every console warning and check the result in a live preview. See how it works or join the waitlist.

Related: useState Explained · JavaScript Array Methods · What Is React? · Browser Developer Tools for Beginners

Keep reading