React’s ‘unique key prop’ warning: why index keys break your UI
Most developers silence the key warning with key={index} and move on. The warning goes away; the bug stays. Keys are how React matches list items across renders, and an index key tells React 'position 2 is always the same item' – which becomes a lie the moment you insert, delete, sort, or filter.
What goes wrong with index keys – concretely
Render a list of rows, each containing an uncontrolled input. Type into row 1, then delete row 0. React sees the same keys (0, 1, …) and reuses the DOM: your typed text now sits next to the wrong row's data. The same mechanism scrambles checkbox state, focus, CSS transitions, and component-local state on reorder. It's one of the hardest bugs to reproduce because it only appears after mutation.
// ❌ Breaks on insert/delete/sort
{todos.map((todo, i) => <TodoRow key={i} todo={todo} />)}
// ✅ Stable identity from your data
{todos.map((todo) => <TodoRow key={todo.id} todo={todo} />)}
Choosing a key when there's no id
- A unique field or combination from the data itself – email, slug, `${userId}-${date}`.
- Generate ids when the item is CREATED (crypto.randomUUID() in the add handler), then keep them – never during render.
- Index is acceptable only when the list never reorders, never filters, and items have no state – a static footer nav, say.
- Never key={Math.random()} – a new key every render remounts every item, destroying state and performance in one move.
Keys as a feature: resetting state on purpose
// Changing the key remounts the component - a clean state reset
<ProfileForm key={selectedUserId} userId={selectedUserId} />
The same mechanism that makes wrong keys dangerous makes deliberate keys powerful. Keying a form by the record it edits guarantees fresh state per record – no manual useEffect resets, no leftover validation errors from the previous user.
A key answers one question: 'is this the same item as last render?' If your key can't answer that truthfully after a sort or delete, it's the wrong key.

