ESC

Type to search the knowledge base.

Keys and Remounting State

Keys control identity during reconciliation: stable ids, index key bugs, and intentional remounts when state must reset.

intermediate3 min read
  • react
  • keys-and

Keys are not “for React performance tips on Twitter.” They are how React matches list children across renders so the right fiber keeps its state, DOM node, and effects. Wrong keys look like mysterious input glitches and teleporting local state.

Docs: Rendering Lists, Preserving and Resetting State.

The rule

Among siblings, each child needs a key that is:

  1. Stable — the same item keeps the same key across renders.
  2. Unique among siblings — not globally unique forever, but unique in that list.
  3. Derived from data identity — usually a database id, not the array index.
// Good
{todos.map((todo) => (
  <TodoRow key={todo.id} todo={todo} />
))}

// Bad when the list inserts, deletes, or reorders
{todos.map((todo, index) => (
  <TodoRow key={index} todo={todo} />
))}

Index keys fail when you unshift, sort, or filter. React reuses the fiber at position 0 for whatever item is now first — so the input state of the old first row sticks to a different todo.

Keys outside lists

Any place React needs to distinguish siblings benefits from keys — including fragments of multiple nodes. You can also force remount by changing a key on purpose:

function UserEditor({ userId }) {
  return <ProfileForm key={userId} userId={userId} />;
}

When userId changes, ProfileForm mounts fresh: new state, new effects. That is the recommended alternative to syncing props into state with effects. See resetting state with key and preserving state on reorder.

What key is not

  • Not a prop your component reads as props.key (it is not passed through).
  • Not a CSS selector.
  • Not a substitute for a missing stable id — if the server sends no id, generate one when the item is created and keep it in your model.
  • Not “Math.random() each render” — that remounts every time and destroys performance and focus.
// Catastrophic
<Row key={Math.random()} />

Reconciliation snapshot

On update, React walks old children vs new:

Situation Result
Same key + same type Update in place; state kept
Same key + different type Remount (type change wins)
Key missing / moved Rematch by key; state follows the key
Index key + reorder State sticks to index, not item
function Board({ cards }) {
  return cards.map((card) => (
    <Card
      key={card.id}
      title={card.title}
      // local useState inside Card follows card.id across drags
    />
  ));
}

Interview out-loud

“Keys give list children a stable identity so reconciliation reuses the correct fiber and state. I use business ids, never random keys, and I avoid index keys when the list can reorder. Changing a key intentionally remounts a subtree to reset state.”

Further reading

Production checklist

Before you ship a change in this area, walk the list out loud:

  1. What is the source of truth for the data on screen?
  2. What happens on remount, route change, and Strict Mode double-invoke?
  3. Which updates are urgent (input) versus deferrable (filter large lists)?
  4. Did you profile before adding memo, context splits, or virtualization?
  5. Is there an accessibility path: keyboard, focus, names, and errors?

If you cannot answer those, the API knowledge will not save the interview or the incident.

Related guides