Skip to content

React Key Bugs That Make the Wrong Row Change

Learn how React keys preserve record identity, why index keys break reordered lists, and how to reproduce a wrong-row bug with a controlled test.

Stable record identity keeps a note with the correct React row

TL;DR: A React key tells React which rendered component belongs to which data record across inserts, deletes, filters, and reordering. In our controlled three-row experiment, an index key moved a note from CASE-441 to CASE-443 when the list reversed. A stable record ID kept the note with CASE-441. If the wrong row changes after a filter or sort, inspect identity before blaming the input, event handler, or API.

A support agent adds a private note to one case. The list is filtered or sorted. The note now appears on another case.

The API payload may be correct. The input component may be correct. The browser may have done exactly what the code asked. The broken part is often the relationship between a rendered row and the record it represents.

In React, that relationship is expressed through the key prop.

This guide explains what a React key does, why array indexes fail in dynamic lists, how to reproduce the wrong-row bug, and what evidence helps the teammate fixing it.

What a React key actually does

A React key is a stable identity label for an item among its siblings. React uses it while reconciling one render with the next.

When a list changes, React has to decide which previous component instance corresponds to each new item. That matters because a component instance can hold local state, focus, selection, pending input, or child state.

A useful key answers this question:

Is this the same underlying record as before, even if its screen position changed?

The official React guidance says keys should come from the data and remain stable. A database ID, case number, order ID, or other domain identifier is usually appropriate.

A key is not:

  • a CSS selector

  • a DOM id

  • a value passed to the child as a normal prop

  • a replacement for application-level state design

  • proof that the backend saved the correct record

It is the identity contract React uses between renders.

Why index keys break changing lists

This pattern looks harmless:

react-key-example-1.jsx

{cases.map((item, index) => (
  <CaseRow key={index} item={item} />
))}

It can be acceptable for a truly static list that will never reorder, filter, insert, or delete. That condition is less common than it sounds.

The index describes a position, not a record. If CASE-441 starts at position 0 and the list reverses, position 0 may now contain CASE-443. React sees the same key, 0, and can preserve the component instance at that position. Local state from the first record can appear under the third record.

Use a stable record identity instead:

react-key-example-2.jsx

{cases.map((item) => (
  <CaseRow key={item.id} item={item} />
))}

Now the component instance follows item.id when the order changes.

The controlled wrong-row experiment

We built a small fake-data React list with three support cases:

Record

Initial position

Local state used in the test

CASE-441

0

customer cannot reproduce after refresh

CASE-442

1

empty

CASE-443

2

empty

The experiment used the same components and browser actions for both versions. Only the key strategy changed.

  1. Load the three cases in the order 441, 442, 443.

  2. Enter the note on CASE-441.

  3. Reverse the visible list to 443, 442, 441.

  4. Read which record now owns the note.

The result was direct:

Key strategy

Record holding the note after reorder

Result

key={index}

CASE-443

wrong record

key={item.id}

CASE-441

expected record

This was a controlled synthetic experiment, not customer data and not a claim that every wrong-row bug is caused by keys. It demonstrates one precise failure mechanism under a repeatable state transition.

The important detail is that the visible text and the local input state followed different identities. A final screenshot can show the wrong note, but it does not show when the identity split occurred.

A practical debugging sequence

When a user reports that an action affected the wrong row, debug the transition rather than only the final screen.

1. Freeze the starting state

Record the exact items, order, filters, sort direction, pagination state, and relevant permissions. If the list came from an API response, keep the record IDs that reached the browser.

2. Identify state ownership

Ask where the incorrect value lives:

  • local component state

  • form-library state

  • a parent object keyed by record ID

  • a global store

  • a server response

  • cached query data

Index-key bugs are especially visible when state lives inside each row component.

3. Capture the first structural change

The useful action is often not the final click. It may be:

  • reversing the sort order

  • applying a filter

  • removing an item

  • inserting a new item above the target

  • moving between pages

  • replacing temporary IDs after a save

The first mismatch is where a component instance becomes attached to the wrong record.

4. Inspect the key at the map site

Do not inspect only the row component. The key is assigned where the collection is rendered.

Look for index keys, random values, timestamps, context-dependent strings, duplicated IDs, or keys that change when display state changes.

5. Rerun with a stable domain ID

Change only the identity strategy and repeat the same browser path. Keep the starting records and actions constant. If the failure disappears, you have isolated the key behavior without claiming a broader fix than the test supports.

Choosing a good React key

Use a value that is unique among siblings and stable for the lifetime of the record.

Candidate

Usually safe?

Why

Database or domain record ID

yes

follows the underlying record across renders

Composite of stable record fields

sometimes

useful when no single stable ID exists

Array index

only for static lists

follows position when the collection changes

Math.random()

no

creates a new identity on each render

Editable label or title

usually no

changes with display content and may not be unique

Temporary client ID

yes, with care

must remain stable until reconciled with the saved record

Do not generate a new key during render. If new records need client-side identity, assign the ID when the record is created and preserve it.

Tests that catch identity bugs

A snapshot test of the initial list will not catch this class of problem. The test has to change the collection while state exists.

At minimum, cover these transitions:

  • type into a row, then sort

  • select a row, then filter

  • expand a row, then insert an item above it

  • edit a row, then delete a sibling

  • create an optimistic row, then replace its server ID

  • restore a removed record and verify state is not silently transferred

Assert the record identity and the state together. For example, verify that the note remains under CASE-441, not merely that a note input still exists.

A browser test should also read the final visible order and the target record ID. That makes the expected and actual result explicit for the engineer reviewing the failure.

What the key fix does not prove

Stable keys fix the component identity contract. They do not establish that:

  • the API updated the correct database row

  • stale cached data was invalidated

  • permissions were applied correctly

  • an optimistic update reconciled with the server

  • every local state bug is resolved

  • a finished regression test now covers the whole workflow

If the stable-key rerun still fails, continue through event payloads, mutation variables, cache updates, and server readback. The identity fix narrows the investigation. It does not replace it.

Preparing browser incidents for solving agents

As solving agents take on more frontend investigation over the next 6 to 18 months, they will need record identity, ordered browser actions, the first state mismatch and a verified final readback, not only a DOM snapshot or a passing rerun.

That changes the incident format. A future investigation should not hand an agent a screenshot labeled "wrong row." It should provide the starting records, the key strategy, the exact reorder or filter action, the expected record, the actual record, the relevant permission boundary, and the result of a bounded rerun.

Teams can prepare now by using stable domain IDs as React keys and preserving a compact incident record with the known start state, exact reorder or filter action, expected record, actual record, permission boundary and final-state readback.

The same structure helps a human engineer today. It also gives a solving agent less room to confuse screen position with record identity later.

Record the transition that moves the state

A wrong-row report becomes useful when the teammate opening it can see the state before the reorder, the action that changed the list, and the first record mismatch.

A deliberately started Samelogic recording can preserve the ordered browser path around the reorder, the affected list item and the failing moment so the teammate opening the recording can compare what changed without treating the recording as automatic root-cause analysis. The current product direction is to make this browser context easier for people and solving agents to inspect, while the controlled React experiment above remains the proof for this specific key behavior.

Install CSS Selector & XPath Finder by Samelogic when you need to deliberately record the browser steps behind a changing-list bug and preserve the relevant element context for the handoff.

Sources

Related workflows

Move from editorial context into the selector, Playwright, and bug-reproduction pages that turn exact UI evidence into action.

Capture browser proof before the handoff gets vague.

Select the exact element, record the replay, and give QA, product, and engineering a test artifact they can act on without another clarification loop.

Install the Chrome Extension
Visual
Semantic
Behavioral

Used by teams at

  • abbott logo
  • accenture logo
  • aaaauto logo
  • abenson logo
  • bbva logo
  • bosch logo
  • brex logo
  • cat logo
  • carestack logo
  • cisco logo
  • cmacgm logo
  • disney logo
  • equipifi logo
  • formlabs logo
  • heap logo
  • honda logo
  • microsoft logo
  • procterandgamble logo
  • repsol logo
  • s&p logo
  • saintgobain logo
  • scaleai logo
  • scotiabank logo
  • shopify logo
  • toptal logo
  • zoominfo logo
  • zurichinsurance logo
  • geely logo