Cuando Vue renderiza una lista con v-for, necesita saber qué elemento del DOM corresponde a qué elemento del array. El atributo :key es ese identificador. Sin él (o con una key incorrecta), Vue toma atajos que pueden provocar bugs reales.
Qué falla sin una key adecuada
Sin una key única, Vue reutiliza los elementos del DOM por posición. Si eliminas el segundo elemento de una lista, Vue no elimina el segundo <li> — actualiza el texto de los elementos 2, 3, 4... y elimina el último. Esto es eficiente para texto simple, pero falla cuando los elementos tienen su propio estado.
<script setup lang="ts">
import { ref } from 'vue'
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
])
function removeFirst() {
items.value.shift()
}
</script>
<template>
<!-- ❌ key=index: tras eliminar Apple, el input que tenía el texto de Apple
ahora aparece junto a Banana — el estado no coincide -->
<div v-for="(item, index) in items" :key="index">
<span>{{ item.name }}</span>
<input placeholder="Escribe algo" />
</div>
<!-- ✅ key=item.id: Vue elimina correctamente el nodo DOM de Apple,
Banana y Cherry conservan sus inputs y su estado -->
<div v-for="item in items" :key="item.id">
<span>{{ item.name }}</span>
<input placeholder="Escribe algo" />
</div>
</template>Escribe algo en cada input y luego elimina el primer elemento. Con key=index, los inputs se mezclan. Con key=item.id, el nodo DOM correcto se elimina y todo lo demás permanece en su sitio.
Las reglas
- Usa siempre
:keyen los elementos conv-for. - Usa un identificador único y estable — un
idde tus datos, una clave primaria de base de datos, un slug. Algo que no cambie entre renderizados. - Nunca uses el índice del array como key si los elementos pueden reordenarse, insertarse o eliminarse. El índice cambia cuando el array cambia, lo que invalida el propósito.
- Las keys deben ser primitivos — strings o números. Los objetos no funcionan.
<!-- ✅ Bien: ID estable de los datos -->
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
<!-- ✅ Bien: string único y estable -->
<li v-for="tab in tabs" :key="tab.slug">{{ tab.label }}</li>
<!-- ❌ Mal: el índice cambia cuando el array cambia -->
<li v-for="(item, i) in items" :key="i">{{ item.name }}</li>Cuándo el índice es válido
Si la lista es estática (nunca se reordena, no hay añadidos ni eliminaciones) y los elementos no tienen estado interno (sin inputs, sin componentes hijo), entonces key=index no causará bugs. Pero usar un ID real es un hábito seguro que no cuesta nada.
Ver también: ¿Cómo funciona el renderizado de listas con v-for? · ¿Cuál es la diferencia entre v-if y v-show?
Referencias
- Mantener el estado con key - Docs de Vue.js
- v-for - Docs de Vue.js
- Renderizado de Listas - Docs de Vue.js