·
2 min read

Vue Tip: Don't Use v-if With v-for

Vue Tip: Don't Use v-if With v-for Image

It can be tempting to use v-if with v-for in these common scenarios: Filtering items in a list and avoiding rendering a list if it should be hidden.

Let's take a detailed look at these two scenarios.

Filtering items in a list

You might want to write the following code to filter items in a list:

Component.vue
<script setup lang="ts"> const todos = [ { id: 1, isComplete: false, name: 'Buy Milk' }, // ... ] </script> <template> <ul> <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo.name }} </li> </ul> </template>

This code will throw an error because the v-if directive will be evaluated first and the iteration variable todo does not exist at this moment. When Vue processes directives, v-if has a higher priority than v-for.

You can fix the code by using a computed property that returns your filtered list:

Component.vue
<script setup lang="ts"> import { computed } from 'vue' const todos = [ { id: 1, isComplete: false, name: 'Buy Milk' }, // ... ] const uncompletedTodos = computed(() => todos.filter((todo) => !todo.isComplete)) </script> <template> <ul> <li v-for="todo in uncompletedTodos" :key="todo.id"> {{ todo.name }} </li> </ul> </template>

In this example, the code doesn't loop through the entire array, only the filtered computed property, which is also cached. In case you have massive arrays, this can be a huge performance improvement.

Avoid rendering a list if it should be hidden

In some cases, you want to render a list only if a certain variable is set:

Component.vue
<script setup lang="ts"> import { ref } from 'vue' const todos = [ { id: 1, isComplete: false, name: 'Buy Milk' }, // ... ] const showTodos = ref(false) </script> <template> <ul> <li v-for="todo in todos" v-if="!showTodos"> {{ todo.name }} </li> </ul> </template>

A better solution would be to move the v-if to a container element (e.g. ul, ol):

Component.vue
<script setup lang="ts"> import { ref } from 'vue' const todos = [ { id: 1, isComplete: false, name: 'Buy Milk' }, // ... ] const showTodos = ref(false) </script> <template> <ul v-if="!showTodos"> <li v-for="todo in todos"> {{ todo.name }} </li> </ul> </template>

StackBlitz Demo

The code of this article is interactively available at StackBlitz:

If you liked this Vue tip, follow me on X to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter:

I will never share any of your personal data. You can unsubscribe at any time.