Back to React
React
easy
mid

Why do React keys matter and what happens when you get them wrong?

Keys give React identity for siblings in a list. Without stable keys, React matches children by position — reordering or inserting mid-list causes wrong component reuse: state, DOM, refs, and focus follow the slot, not the data. Use a stable, unique id from the data; never the array index unless the list is append-only and uneditable.

5 min read·~10 min to think through

Keys are how React tells siblings apart during reconciliation. They are not for performance — they're for correctness.

Without keys, React matches by position.

Say you render:

tsx
items.map(item => <Row item={item} />)

React sees children at positions 0, 1, 2. On the next render, the new positions 0, 1, 2 are diffed against the old. If you prepend a new item, every old row gets shifted into the slot of a different item — but React doesn't know it's a different item; it reuses the same fiber, the same DOM, the same state.

The visible bug:

  • A row had focus on its input. Insert above → focus is now on a different row, same position.
  • A row had collapsed state. Reorder → the collapsed state stays on the slot, not the item.
  • Animations restart on the wrong items.
  • Inputs show the wrong values because uncontrolled inputs keep their DOM state.

The fix: stable, unique key.

tsx
items.map(item => <Row key={item.id} item={item} />)

Now React diffs by id: it can detect "row with id 7 moved from position 2 to position 0" and rearrange the existing fiber/DOM. State, refs, and focus follow the data.

Why key={index} is usually wrong.

tsx
items.map((item, i) => <Row key={i} ... />)

Index-as-key is equivalent to no key — it just suppresses the warning. The keys still describe positions. Only safe when:

  1. The list is append-only and never reordered.
  2. List items are stateless (no input, no internal state).
  3. No animations or transitions tied to identity.

If any of those is false, use a stable id.

Common cases where the bug bites.

  • Reorderable to-do lists (drag-and-drop).
  • Filter / sort / search — items appear/disappear in arbitrary positions.
  • Virtualized lists — rows scroll into view; index-as-key makes them inherit the wrong state.
  • Forms with dynamic field arrays — first-row bug: removing the first row makes inputs show the second row's value.

Generating keys when no id exists.

  • Best: persistent server id (item.uuid, primary key).
  • Acceptable: deterministic hash of contents (when contents are unique).
  • Last resort: generate a UUID at creation time and store it on the item.
  • Never: key={Math.random()} — fresh key every render, full unmount + remount of every row.

key to intentionally reset state.

The other side of keys: changing a key on the same component forces a remount. Useful when you want to reset a subtree's state cleanly:

tsx
<EditForm key={selectedItem.id} item={selectedItem} />

When selectedItem changes, the form resets — no useEffect(() => setState(...), [item]) boilerplate.

Keys must be unique among siblings, not globally. Two siblings with the same key is a bug; the same key in a different parent is fine.

Senior takeaway. Keys aren't a perf optimization. They're React's identity contract for siblings. The performance benefit (less DOM work on reorder) is downstream of correctness. The interview answer is: keys identify children across renders so React can preserve state and reuse DOM correctly when the list changes.

Follow-up questions

  • Why is `key={index}` sometimes a bug but sometimes fine?
  • How can changing a key be used as a feature?
  • Why isn't `key={Math.random()}` a valid key strategy?
  • How does reconciliation use keys during a list reorder?

Common mistakes

  • Using array index as key on a reorderable / filterable list.
  • Using `Math.random()` as key — forces remount every render.
  • Putting keys on the wrapping `<div>` inside `Row` instead of on `<Row>` itself.
  • Duplicate keys among siblings — React warns and falls back to position.

Performance considerations

  • Stable keys let React move DOM nodes (cheap) instead of unmounting + remounting (expensive).
  • Fresh keys (Math.random) force a full subtree rebuild every render.

Edge cases

  • Adding items at the start of a list is the most common reveal-of-key-bug.
  • Virtualized lists — must use item id, not visible index.
  • Lists with duplicate content but distinct identity (two rows with same name, different id) — use the id.

Real-world examples

  • Any drag-and-drop reorder list. Form arrays in react-hook-form. Comment threads.

Related questions