Why are keys critical in v-for, and what breaks without them?

LowIntermediateVue
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

Explain why :key is required/recommended in v-for, what Vue uses it for during virtual DOM diffing, and give concrete examples of bugs you can get when keys are missing or unstable (including why using the index as key is usually wrong).

Answer

Core idea

:key gives Vue a stable identity for each rendered item. During updates, Vue matches old VNodes to new VNodes primarily by type + key. With good keys, Vue can move/reuse the right DOM nodes and component instances. Without keys, Vue falls back to a mostly index-based / in-place patch strategy, which can reuse the wrong DOM/component for a different item.

Scenario

With stable keys (e.g., item.id)

Without keys / bad keys (e.g., index)

Insert/remove in the middle

Vue inserts/removes the correct node and keeps other items' DOM/state attached

Vue reuses DOM nodes by position; DOM/state may “shift” to the wrong item

Sorting / reordering

Vue moves existing nodes (minimal DOM work) and preserves per-item state

Vue patches in place; items can display wrong state (inputs, toggles, child state)

Stateful child components in a list

Each child instance stays with its data item

Child instances can get reassigned to different data items (state leak/mismatch)

Transitions (<transition-group>)

Correct enter/leave/move animations

Animations glitch or don’t run correctly (Vue can’t track moves reliably)

What keys enable vs what breaks without them

What “breaks” in practice

Typical real bugs when keys are missing or unstable:
Inputs show the wrong value after sorting/filtering because the DOM input element is reused for a different item.
Focus/caret jumps (typing in an input, list updates, and the focused DOM node is reused/moved incorrectly).
Checkboxes/toggles flip on the wrong row (DOM state vs data association breaks).
Child component local state leaks (e.g., expanded/collapsed, internal caches, timers) to a different item after reordering.
Transition-group move animations break because Vue can’t reliably compute “this node moved from A to B” without keys.

HTML
<!-- BAD: no key (or :key="index") can cause DOM/state to shift on reorder -->
<script setup>
import { ref } from 'vue';

const items = ref([
  { id: 1, name: 'Alpha' },
  { id: 2, name: 'Bravo' },
  { id: 3, name: 'Charlie' }
]);

function reverse() {
  items.value = [...items.value].reverse();
}
</script>

<template>
  <button @click="reverse">Reverse</button>

  <ul>
    <li v-for="(item, index) in items">
      <!-- Try typing in Alpha, then Reverse: the typed DOM input may appear under a different item -->
      <label>
        {{ item.name }}
        <input placeholder="type here" />
      </label>
    </li>
  </ul>
</template>
                  
HTML
<!-- GOOD: stable key preserves DOM association and component instances -->
<script setup>
import { ref } from 'vue';

const items = ref([
  { id: 1, name: 'Alpha' },
  { id: 2, name: 'Bravo' },
  { id: 3, name: 'Charlie' }
]);

function reverse() {
  items.value = [...items.value].reverse();
}
</script>

<template>
  <button @click="reverse">Reverse</button>

  <ul>
    <li v-for="item in items" :key="item.id">
      <label>
        {{ item.name }}
        <input placeholder="type here" />
      </label>
    </li>
  </ul>
</template>
                  

Index as key: when it’s wrong vs acceptable

:key="index" is effectively “no stable identity” when the list can reorder, filter, insert, or delete. It tells Vue: “identity is position”, so Vue will keep DOM/state by index, not by data item. It’s only acceptable when the list is truly static (never changes order/length) or when each row has no meaningful state (rare in real apps).

Key rule

Why

Example

Use a stable unique id

Preserves identity across reorder/insert/delete

:key="item.id"

Don’t use random/unstable keys

Forces full destroy/recreate every render (kills performance/state)

:key="Math.random()" (bad)

Avoid index for dynamic lists

Index changes when list changes, so identity shifts

:key="index" (usually bad)

Key must be primitive + stable

Vue compares keys; stable primitives keep matching predictable

number/string id

transition-group needs keys

Move/enter/leave tracking depends on identity

<transition-group> children must be keyed

Best practices for :key in v-for

Interview-ready takeaway

Keys aren’t about “performance only”. They’re primarily about correctness: ensuring DOM nodes and component instances stay attached to the same logical data item across updates. Without stable keys (or with index keys), you risk state/DOM mismatches when the list changes.

Similar questions
Guides
30 / 34