Vue v-for

The v-for directive repeats a template block once for every item in an array, object, or range, making it the standard way to render lists in Vue.

The v-for Directive

v-for uses a special syntax in the form item in items, where items is the source array from your data and item is an alias for the element currently being iterated. Vue renders the element it's attached to once per entry in the array, in order, and keeps the DOM in sync whenever the array changes.

Basic v-for over an array

<div id="app">
  <ul>
    <li v-for="fruit in fruits" :key="fruit">
      {{ fruit }}
    </li>
  </ul>
</div>

<script>
const app = Vue.createApp({
  data() {
    return {
      fruits: ['Apple', 'Banana', 'Cherry', 'Dragon fruit']
    }
  }
})
app.mount('#app')
</script>

Why Every v-for Needs a :key

Vue reuses and reorders existing DOM nodes for performance instead of rebuilding the whole list from scratch. The :key attribute gives each node a stable identity so Vue can tell which item moved, which was added, and which was removed, rather than guessing based on position.

  • Always bind :key to something unique per item, such as a database id.
  • Never use the array index as the key if items can be reordered, inserted, or removed.
  • Keys should be primitive values (strings or numbers), not objects.
  • A missing :key still renders, but you may see stale input values or transition glitches when the list changes.
  • Keys only need to be unique among siblings in the same v-for, not globally.

v-for with index and :key

<div id="app">
  <ol>
    <li v-for="(task, index) in tasks" :key="task.id">
      {{ index + 1 }}. {{ task.label }}
    </li>
  </ol>
</div>

<script>
const app = Vue.createApp({
  data() {
    return {
      tasks: [
        { id: 101, label: 'Write the proposal' },
        { id: 102, label: 'Review the budget' },
        { id: 103, label: 'Send the invite' }
      ]
    }
  }
})
app.mount('#app')
</script>
Note: Heads up: the index in (task, index) always reflects the item's current position in the rendered list, not some permanent id it was born with. If the array gets sorted, the index shuffles right along with it.

Looping Over Objects and Number Ranges

v-for isn't limited to arrays. Point it at a plain object and Vue will iterate its own enumerable properties, optionally exposing the value, key, and index. Point it at a plain integer n and Vue will loop from 1 through n, which is handy for star ratings, pagination dots, or generating placeholder rows.

ExpressionWhat you get
item in itemsjust the value of each array element
(item, index) in itemsthe value plus its position, starting at 0
(value, key) in objeach property's value and its property name
(value, key, index) in objvalue, property name, and enumeration order
n in 5the integers 1, 2, 3, 4, 5

v-for over object properties

<div id="app">
  <ul>
    <li v-for="(value, key) in product" :key="key">
      <strong>{{ key }}:</strong> {{ value }}
    </li>
  </ul>
</div>

<script>
const app = Vue.createApp({
  data() {
    return {
      product: {
        name: 'Mechanical Keyboard',
        price: 89,
        inStock: true
      }
    }
  }
})
app.mount('#app')
</script>
Note: Avoid combining v-for and v-if on the same element. Vue evaluates them with different precedence depending on the syntax used, and mixing them tends to produce lists that filter unpredictably. Filter the array first, in a computed property, and loop over the result instead.

Exercise: Vue v-for

Why does Vue recommend providing a unique :key when using v-for?