Skip to main content

Keep stable keys when a React list can reorder

2026-11-11 16:36:28.500028+00:00
Keep stable keys when a React list can reorder

A row key tells React which item a rendered child belongs to. When a list can be sorted, the item's position is a poor identity. A contact that moves from the first row to the last should keep its own edit state.

Use synthetic contacts with stable IDs assigned when they are created. Render the sorted result without changing those IDs.

const ordered = [...contacts].sort(
  (a, b) => a.name.localeCompare(b.name)
);

return ordered.map(contact => (
  <ContactRow key={contact.id} contact={contact} />
));

The array copy prevents sorting from mutating the caller's data. In this component excerpt, ContactRow may contain a locally controlled nickname field. React's list guide explains that keys let React match items when their positions change.

Rows rearranging while ID tags remain attached.

Make the failure visible

Create contacts named Bea and Arun. Type a distinctive draft value into Bea's row, then sort by name. With stable IDs, the draft should stay attached to Bea. Repeat using the row index as the key in a disposable fixture. Depending on how the row owns its input, React may preserve state at the old position and show the draft alongside Arun.

Deletion exposes the same problem. Remove the first contact while editing a later one. Check the focused field, pending validation and expanded details, not only the text rendered from props. A list that looks correct when every cell is stateless can fail as soon as row state appears.

Keys need to be unique among siblings. They do not need to be globally unique across the whole application. If a displayed list combines records from several systems whose IDs overlap, use a stable composite identity that includes the source. Avoid using a contact name, since names can change and two contacts can share one.

A key is consumed by React. If ContactRow needs the identifier for saving, pass it through the contact prop or a separate id prop as well. Reading props.key will not provide it.

Generating a fresh UUID during every render defeats matching and remounts rows repeatedly. Generate identity once when creating a client-only item, then keep it with that item until the server returns a durable identity. A server-ID replacement can itself remount the row, so plan that transition if it contains an unsaved draft.

Stable keys preserve the association between a record and its component instance. They cannot repair duplicate records, an incorrect sort function or a save handler that submits the wrong contact ID.