Skip to content

List Rendering Caveats

You’ve already seen how to render lists using the v-for directive. Here are some key points to keep in mind about list rendering.

Array Change Detection

Mutation Methods

Vue detects changes when a reactive array is modified using these mutation methods:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

Using these methods triggers automatic DOM updates.

Replacing an Array

Non-mutating methods like filter(), concat() and slice() do not modify the original array, but return a new array. When using these, replace the old array with the new array:

javascript
// `items` is a ref with array value
items.value = items.value.filter((item) => item.message.match(/Foo/))

You might think this will cause Vue to throw away the existing DOM and re-render the entire list - luckily, that is not the case. Vue implements some smart heuristics to maximize DOM element reuse, so replacing an array with another array containing overlapping objects is a very efficient operation.

Displaying Filtered/Sorted Results

Sometimes you may want to show a filtered or sorted view without changing the original array. The best approach in this case is to use a computed property:

vue
<script setup>
import { computed, ref } from 'vue'

const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})
</script>

<template>
  <li v-for="n in evenNumbers" :key="n">{{ n }}</li>
</template>

In situations where computed properties are not feasible (e.g. inside nested v-for loops), you can use a method:

vue
<script setup>
import { ref } from 'vue'

const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])

function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}
</script>

<template>
  <ul v-for="numbers in sets">
    <li v-for="n in even(numbers)" :key="n">{{ n }}</li>
  </ul>
</template>

Be careful with reverse() and sort() in a computed property! These two methods will mutate the original array, which should be avoided in computed getters. To avoid this, create a copy first:

diff
- return numbers.reverse()
+ return [...numbers].reverse()