Home/Learn/React/Lists and Keys

Lists and Keys

Beginner
Fundamentals

A key tells React which item is which between renders. Using the array index is the single most common source of "the wrong row updated" bugs.

Overview

Rendering a list is a map call, which everybody gets right immediately. The key is the part that goes wrong. React uses keys to match elements from the previous render to the next one, deciding what to keep, move, or destroy. If the key is the array index, then removing the first item shifts every key by one, so React believes each row became a different row — and any state inside those rows, including what the user typed and where the cursor was, stays with the position rather than the data.

Rendering a List

map plus a stable key from the data itself.

map, with a key from the data
<ul>
  {problems.map(problem => (
    <ProblemRow key={problem.id} problem={problem} />
  ))}
</ul>

// The key goes on the outermost element of the map, not inside it
{rows.map(r => <Fragment key={r.id}><dt>{r.k}</dt><dd>{r.v}</dd></Fragment>)}

// Keys must be unique among SIBLINGS, not globally.
// Two different lists can both use key={1}.

// Filtering and sorting are just JavaScript
{problems
  .filter(p => p.difficulty === level)
  .sort((a, b) => a.order - b.order)
  .map(p => <ProblemRow key={p.id} problem={p} />)}

// Empty state, always
{problems.length === 0
  ? <EmptyState />
  : problems.map(...)}

Why the Index Breaks

A concrete failure, because the abstract rule never convinces anyone.

Delete the first row and watch state follow position
// A list of inputs, keyed by index
{todos.map((todo, i) => <TodoInput key={i} todo={todo} />)}

// State: ['Buy milk', 'Call Ravi', 'Ship release']
// keys:  0            1            2

// Delete 'Buy milk'. Now:
// State: ['Call Ravi', 'Ship release']
// keys:  0             1

// React sees key 0 still exists and keeps the DOM node and its
// internal state. The text the user had typed into row 0 — and the
// focus, and the checkbox state — are now attached to 'Call Ravi'.

// With key={todo.id}, React sees the id for 'Buy milk' is gone,
// removes exactly that row, and every other row keeps its own state.

// The index is safe ONLY when the list never reorders, never has
// items inserted or removed, and the rows hold no state.

Finding a Stable Key

What to use when the data has no id.

Stable ids, and key-as-reset
key={problem.id}                          // best — a real id
key={`${row.userId}-${row.topic}`}       // a composite natural key
key={item.slug}

// Avoid: Math.random() and crypto.randomUUID() inline —
// a new key every render means React destroys and rebuilds the
// entire list each time. It "fixes" the warning and ruins performance.
key={Math.random()}                       // never

// If the data genuinely has no id, generate one when the item is
// created and store it with the item:
setRows(rows => [...rows, { id: crypto.randomUUID(), value: '' }])

// A changing key is also a deliberate tool: change the key on a
// component to reset all of its internal state.
<ProblemEditor key={problem.slug} problem={problem} />
// Switching problems now gives a clean editor rather than the
// previous problem's draft.

Key Points to Remember

  • 1Keys let React match elements between renders; they must be unique among siblings, not globally
  • 2An index key attaches state to a position, so deleting or reordering moves state onto the wrong item
  • 3Index keys are safe only for a static list that never reorders and whose rows hold no state
  • 4A random key regenerated each render destroys and rebuilds the whole list
  • 5Changing a component's key deliberately resets all of its internal state

Interview Questions

Sign in to ask Aria
1

What does React use the key prop for?

Easy
2

Give a concrete bug caused by using the array index as a key.

Medium
3

How can you force a component to reset all of its state?

Medium

Ask Aria about Lists and Keys

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…