Vue list rendering uses the v-for directive to display repeated elements from an array, object, or numeric range. Whenever the source array changes, Vue updates the rendered list automatically.
To update lists efficiently without losing input state or animations, Vue relies on a unique :key attribute on each looped item. The key acts as a stable ID so Vue can track each item across insertions, deletions, and sorting.
flowchart TD
Data["Reactive Array (tasks)"] --> Loop["v-for='task in tasks' :key='task.id'"]
Loop --> VDOM["Virtual DOM Nodes with Keys"]
VDOM --> Patch["Targeted Reorder and Patch"]
v-for Syntax Formsv-for="(item, index) in items" :key="item.id"v-for="(value, key, index) in userSettings" :key="key"v-for="page in totalPages" :key="page" (starts at 1)<template v-for="item in items" :key="item.id">This task manager uses v-for with task.id as :key and mutates or replaces the reactive array inside <script setup>.
<script setup>
import { ref } from 'vue';
const tasks = ref([
{ id: 101, text: 'Review Vue 3 Documentation', completed: true },
{ id: 102, text: 'Build Auth Composable', completed: false },
{ id: 103, text: 'Write Component Tests', completed: false }
]);
function toggleTask(id) {
const task = tasks.value.find((item) => item.id === id);
if (task) task.completed = !task.completed;
}
function removeTask(id) {
tasks.value = tasks.value.filter((item) => item.id !== id);
}
</script>
<template>
<div class="todo-app">
<h2>Task List</h2>
<ul>
<li
v-for="task in tasks"
:key="task.id"
:class="{ done: task.completed }"
>
<span>{{ task.text }}</span>
<button @click="toggleTask(task.id)">Toggle</button>
<button @click="removeTask(task.id)">Remove</button>
</li>
</ul>
</div>
</template>
<style scoped>
.done span {
text-decoration: line-through;
color: #64748b;
}
</style>
index as :key on Dynamic Lists: If you delete, insert, or reorder items, :key="index" shifts every remaining key and mixes up child component state. Always use a unique ID like task.id.:key: Keys must be primitive strings or numbers, not plain objects. Passing an object turns every key into "[object Object]".v-for with v-if: Putting v-if="task.completed" on the same tag as v-for fails in Vue 3 because v-if runs first. Compute a filtered array instead.Mutation methods like push(), pop(), shift(), unshift(), splice(), sort(), and reverse() trigger updates automatically. Replacing tasks.value with .filter() or .map() also works cleanly.
:key when using <template v-for>?Place :key directly on the <template v-for> tag itself rather than duplicating it on child elements inside the template block.
v-for iterate over a component instead of an HTML tag?Yes. Write <TaskCard v-for="task in tasks" :key="task.id" :task="task" /> and pass the item explicitly through props.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
You've completed this section! Take a quick 5-question quiz to check your understanding.