Text typed into one row appears under a different row after insert
Vue incident
Draft note jumps to the wrong row after adding at the top
The list updates, but edit state and draft text jump to the wrong row because `v-for` is keyed by index instead of item identity.
Failure signals
The clues you start with
Edit mode sticks to the wrong item after reordering
The list uses `:key="index"`
Overview
What failed in production?
Start by making the bug legible: what the user saw, why it mattered, and what the product was doing underneath.
Symptom
A manager starts editing a note for Maya, then adds a new teammate to the top of the list. The typed draft is now shown under Noah instead, even though Maya was the one being edited.
User impact
Users stop trusting inline editing because the UI makes it look like the wrong person is being changed.
Environment
Vue 3 team-management screen with editable rows. The list can prepend, filter, and sort members, and each row has local edit state and a draft note input.
Evidence pack
What you can inspect before answering
What the user sees
As soon as a new row is inserted at the top, the draft and edit mode appear under a different teammate, even though no save happened.
Render clue
The row component instance is being reused for the new position instead of staying attached to the same logical teammate.
Current list rendering
<MemberRow
v-for="(member, index) in members"
:key="index"
:member="member"
/>
<button @click="members.unshift(createMember())">
Add to top
</button>Stage 1: likely root cause
Why does the draft jump to the wrong row?
Pick the explanation that matches Vue list identity, not just generic form bugs.
The issue is not generic input caching. The stronger clue is that the wrong row identity is being reused.
Animation can affect timing, but it does not explain why edit state moves to a different logical item.
Correct. In Vue, keys represent identity. `index` tracks position, so inserts and reorders make row state jump to the wrong item.
`v-model` is fine here. The problem is the unstable key, not the fact that the input lives in a list.
Stage 2: debug priority order
What would you check first? Put the best three at the top.
First prove that the wrong logical row is being reused after the insert.
- #1Reproduce with one row in edit mode, then insert at the top and observe which logical item keeps the draft.Confirm the exact user-facing failure.
- #2Inspect the `v-for` key and confirm whether it is based on `index` or a stable item id.Identity mistakes usually start here.
- #3Try a quick repro with `:key="member.id"` and see whether the draft stays with the right row.A minimal change can confirm the root cause fast.
- #4Add `nextTick()` around the insert path before confirming the identity bug.Weak first move.
- #5Force the whole list to remount on every update before measuring what Vue is reusing.Masks the bug instead of explaining it.
Only the top 3 positions are scored.
Expected: Reproduce with one row in edit mode, then insert at the top and observe which logical item keeps the draft.
Expected: Inspect the `v-for` key and confirm whether it is based on `index` or a stable item id.
Expected: Try a quick repro with `:key="member.id"` and see whether the draft stays with the right row.
Stage 3: best fix set
Which changes belong in the real fix?
Pick the changes that keep row state attached to the correct logical item.
Correct. This gives Vue the real identity signal it needs.
That hides the symptom by destroying user work, but it does not fix the wrong identity mapping.
Correct. Any longer-lived row state should follow the item id, not the row index.
Correct. Stable identity is the key design rule here.
That wipes local state and performance to hide the bug. The right fix is stable keys.
Stage 4: regression guard
What is the best guardrail for this bug?
Pick the check that would catch row identity drifting after list inserts or reorders.
Correct. This directly guards the identity bug users felt.
That can miss the bug because refresh is one of the things that resets the wrong local row state.
The core bug is local list identity, not network timing.
Inline editing is not the problem. The wrong key is.
You saw the wrong row update, but not the identity bug
The UI looked flaky, but the real issue was still how the list keyed item identity.
Good instincts, but one fix still treated the symptom
You connected the bug to list rendering, but one step still leaned on resets or remounts.
You diagnosed the row-identity bug clearly
You found the unstable key, chose a stable identity fix, and protected the exact user-facing failure.
Ideal runbook
How a strong answer should flow
- 1Reproduce the bug with one row in edit mode and one insert at the top.
- 2Check whether the list is keyed by position or by stable item id.
- 3Switch to stable keys and keep longer-lived row state tied to item ids.
- 4Lock the fix with a test that inserts or reorders while one row is being edited.
Reflection
Reflection note
When is using `index` as a key actually safe, and why is that rare in real apps?
Vue shortcut
When row state jumps after insert, reorder, or filter, ask immediately whether the key represents identity or only position.
Strong answer signals
- You explain that keys represent identity, not order.
- You mention local row state and DOM reuse, not just 'Vue rerendered weirdly'.
- You fix the key instead of forcing remounts.