Everyone's seen the warning. Warning: Each child in a list should have a unique "key" prop. You slap key={index} on it, the warning goes away, and everything looks fine. Then in an interview someone asks why that's a problem, and "it just wants a unique value" isn't the answer they're waiting for.
I kept giving the shallow version of this, so I wrote out what's actually going on.
The question that gets asked
Usually some version of:
"You've got a list of inputs rendered from an array. You use the array index as the key. A user types in the second box, then you delete the first item from the list. What happens, and why?"
The answer people give: "the list re-renders." True and useless. The answer they want walks through what React does with the keys.
What keys are actually for
When your state changes, React builds a new tree of elements and compares it to the old one. For a list, it has to match up old elements with new ones so it knows what to reuse, what to update, and what to throw away.
The key is how it does that matching. Not position, not order. The key.
- Same key at that spot across two renders means "same element, just update its props"
- A different key means "different element, unmount the old one, mount a new one" So the key's whole job is to be a stable identity for one item across renders.

Why index breaks it
Index isn't tied to the item -- it's tied to the slot.
Say you render three items with key={0}, key={1}, key={2}. You delete the first one. Now there are two items, and they render with key={0} and key={1}.
React looks at slot 0: old key was 0, new key is 0. Same key, so it keeps that DOM node and just swaps in the second item's props. It doesn't move anything, it rewrites in place. Every remaining item shifts up by one identity while its DOM node stays put.
For plain text that just looks like it worked. The damage shows up the moment a node holds state React doesn't control.
The input bug
This is the example that makes it click:
const [items, setItems] = useState(["a", "b", "c"]);
return items.map((item, index) => (
<li key={index}>
{item}
<input type="text" defaultValue={item} />
</li>
));
Type "hello" into the input for b. Now delete a from the array.
You'd expect the "hello" input to disappear with b, or stay attached to b. Instead React sees key={0} still there, keeps that first DOM node, and the text you typed for b is now sitting in the input for what is now the first item. The uncontrolled input kept its DOM value, and React just re-pointed the label next to it.
Swap in a real stable id:
<li key={item.id}>
Now deleting a means the key a is gone. React unmounts that whole , input and all, and leaves b and c exactly as they were, typed text included.
The follow-up that catches people
"Is index ever fine as a key?" Yeah, actually. If the list never reorders, never has items inserted or removed anywhere but the end, and the items hold no local state or effects, index is fine. A static nav menu. A table of read-only rows. The bug needs list mutation plus per-item state to actually bite. Say that out loud and it shows you understand the mechanism, not just the rule.
Quick recap
- Keys are how React matches old elements to new ones during reconciliation, nothing to do with position
- Same key means reuse the DOM node and update props; different key means unmount and remount
- Index keys track the slot, not the item, so deleting or reordering makes React update the wrong nodes in place
- The bug is invisible with plain text and obvious the second an input, focus, or component state is involved
- Index is genuinely fine for lists that never change order or length and hold no per-item state
Once you've watched the wrong text land in the wrong input, you don't reach for key={index} on a mutable list again. Which is why it's worth reproducing once instead of just nodding at the lint warning.




Comments
No comments yet — be the first to share your thoughts.