v-for iterates over arrays, objects, numbers, and strings to render a list of elements. It works like a for...of loop in JavaScript, but inside the template.
Arrays
<template>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
])
</script>The second argument gives you the index:
<li v-for="(item, index) in items" :key="item.id">
{{ index }}. {{ item.name }}
</li>Objects
<template>
<div v-for="(value, key, index) in user" :key="key">
{{ index }}. {{ key }}: {{ value }}
</div>
</template>
<script setup>
const user = { name: 'Ana', role: 'Dev', level: 'Senior' }
</script>Ranges
<!-- Renders 1 through 5 -->
<span v-for="n in 5" :key="n">{{ n }}</span>Why :key matters
Without :key, Vue reuses DOM elements by position. This breaks when items are reordered, removed, or inserted in the middle, because component state and DOM state get out of sync.
<!-- Wrong: index as key has the same problem as no key on reorder -->
<li v-for="(item, index) in items" :key="index">...</li>
<!-- Right: unique, stable identifier -->
<li v-for="item in items" :key="item.id">...</li>v-for on template
When you need to render multiple elements per iteration without a wrapper:
<template>
<ul>
<template v-for="item in items" :key="item.id">
<li>{{ item.name }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
</template>v-for with components
<template>
<UserCard
v-for="user in users"
:key="user.id"
:user="user"
@remove="removeUser(user.id)"
/>
</template>Props are not automatically injected from the iteration. You have to bind them explicitly.
Mutating vs replacing arrays
Vue detects calls to mutation methods (push, pop, shift, unshift, splice, sort, reverse) on reactive arrays and updates the DOM. For non-mutating methods (filter, map, concat), assign the result back to the ref:
// Mutation: Vue detects this
items.value.push({ id: 4, name: 'Date' })
// Replacement: assign the new array
items.value = items.value.filter(i => i.name !== 'Banana')See also: What's the purpose of :key in v-for?
References
- List Rendering - Vue.js docs
- v-for - Vue.js docs