Explain why keys are required in v-for, how Vue uses them during virtual DOM diffing, and what subtle but serious bugs happen when you use array index as a key (state reuse, wrong DOM nodes, broken animations, and input mix-ups).
Why are keys critical in v-for? What exactly breaks when you use array index as a key?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Overview
Keys are how Vue identifies which virtual DOM node corresponds to which real DOM node across renders. If keys are stable and unique, Vue can correctly reuse, move, or destroy elements. If keys are wrong (or you use the array index), Vue will reuse the wrong DOM nodes, causing bugs like inputs swapping values, components keeping the wrong state, and animations glitching.
1. What Vue actually does when a list changes
When a list rendered with v-for changes, Vue does not recreate the whole DOM. It diffs the old list and the new list and tries to match items using their key.
With good keys | With bad keys (or index) |
|---|---|
Vue moves the correct DOM nodes | Vue reuses DOM nodes for the wrong items |
Component state stays with the right item | Component state gets attached to the wrong item |
Minimal DOM operations | Unpredictable patches and visual glitches |
2. The correct pattern
Always use a stable, unique ID that represents the identity of the item, not its position.
<li v-for="todo in todos" :key="todo.id">
<TodoItem :todo="todo" />
</li>
3. The tempting but dangerous pattern
Using the array index looks fine… until the list changes.
<!-- ❌ Bad -->
<li v-for="(todo, index) in todos" :key="index">
<TodoItem :todo="todo" />
</li>
4. What exactly breaks when you use index as key?
Operation | What you expect | What actually happens with index key |
|---|---|---|
Insert item at top | New DOM node is created at top | Vue reuses every existing DOM node and just shifts data |
Remove an item | That item’s DOM and component state are destroyed | A different item’s DOM gets reused and keeps stale state |
Reorder list (sort, drag & drop) | Items move with their DOM and state | DOM stays in place, only data changes → state mismatch |
Edit inputs in a list | Each input keeps its own value | Values appear to jump to other rows |
5. The classic horror demo: inputs swapping values
<div v-for="(user, i) in users" :key="i">
<input v-model="user.name" />
</div>
<button @click="users.unshift({ name: '' })">Add to top</button>
When you click the button, the text you typed appears to move to a different row. Vue reused DOM nodes by position, not by identity.
6. Why Vue can’t magically fix this
Vue has no way to know whether an item at index 0 is “the same logical item as before” or a different one. The key is the only identity signal you give to the diffing algorithm.
7. When is index as key actually safe?
Almost never — but technically OK if all of these are true:
- The list is static (no insert/remove/reorder).
- The list items have no internal state (no inputs, no components with state).
- You will never filter or sort it.
8. How this relates to performance
Good keys don’t just fix bugs — they also make updates faster because Vue can move nodes instead of destroying and recreating them.
Mental model: keys tell Vue “who is who”. If you lie about identity (using index), Vue will faithfully do the wrong thing — very efficiently.
Summary
- Keys define the identity of list items for Vue’s diffing algorithm.
- Using index as key ties identity to position, not to the actual item.
- This causes state to stick to the wrong DOM nodes: broken inputs, wrong component state, glitchy animations.
- Always use a stable, unique ID from the data itself.