Chapter 2 of 4

Choosing Keys

What keys are for and why the array index is usually the wrong choice.

A key tells React which element in the new list corresponds to which element in the old list. Without it, React can only match by position, and position changes whenever items move.

What a good key looks like

  • Stable - the same item keeps the same key across renders.
  • Unique among siblings - it does not need to be globally unique.
  • Predictable - derived from the data, not generated during render.
{items.map((item) => <Row key={item.id} item={item} />)}

Why the index breaks

Using the index means the key describes a position, not an item. Insert something at the top and every key shifts by one, so React thinks every item changed.

// Before: ["Milk", "Eggs"]  keys 0, 1
// Add "Bread" at the front
// After:  ["Bread", "Milk", "Eggs"]  keys 0, 1, 2
//
// React now thinks item 0 changed from Milk to Bread,
// item 1 from Eggs to Milk, and item 2 is new.

With plain text that is only wasteful. With components holding state - a checked box, a half-typed input, an open menu - that state ends up attached to the wrong row.

When there is no id

Generate ids when you create the items, not while rendering. A key created during render is new every time, which defeats the purpose entirely.

// Wrong: a fresh key on every render remounts everything
{items.map((item) => <Row key={Math.random()} item={item} />)}

// Right: assign an id when the item is created
function addItem(text) {
  setItems([...items, { id: crypto.randomUUID(), text }]);
}