Vue.js v-for Directive
Introduction
The v-for
directive is one of Vue.js's most powerful built-in directives. It allows you to render multiple elements based on an array or object's data. Whether you're creating a simple list, a complex table, or iterating through object properties, v-for
makes it easy to dynamically create repeating elements in your Vue applications.
In this guide, we'll explore how to use the v-for
directive effectively, understand its syntax, learn about key management, and see practical examples of how it can be used in real applications.
Basic Syntax
The basic syntax of v-for
follows this pattern:
<element v-for="item in items">
{{ item }}
</element>
Where:
items
is the source data (an array or object)item
is the alias for the current element being iterated over
Rendering Arrays
Let's start with a simple example of rendering an array of strings:
<template>
<div>
<h2>Programming Languages</h2>
<ul>
<li v-for="language in languages" :key="language">
{{ language }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
languages: ['JavaScript', 'Python', 'Java', 'Ruby', 'Go']
}
}
}
</script>
Output:
- JavaScript
- Python
- Java
- Ruby
- Go
Accessing the Index
You can also access the current index in the iteration:
<li v-for="(language, index) in languages" :key="language">
{{ index }}: {{ language }}
</li>
Output:
- 0: JavaScript
- 1: Python
- 2: Java
- 3: Ruby
- 4: Go
Using v-for
with Objects
v-for
works with objects too, allowing you to iterate over an object's properties:
<template>
<div>
<h2>User Profile</h2>
<ul>
<li v-for="(value, key, index) in userProfile" :key="key">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
userProfile: {
name: 'John Doe',
age: 30,
email: '[email protected]',
location: 'New York'
}
}
}
}
</script>
Output:
- 0. name: John Doe
-
- age: 30
-
- email: [email protected]
-
- location: New York
The Importance of :key
You might have noticed the :key
attribute in our examples. When using v-for
, Vue strongly recommends providing a unique key for each item:
<li v-for="item in items" :key="item.id">
<!-- content -->
</li>
The :key
attribute helps Vue identify which items have changed, been added, or removed. This leads to more efficient DOM updates and helps avoid subtle rendering issues.
Why keys are important:
For example, if you have a list of todo items and you remove one in the middle:
- Without keys: Vue might just update the content of each element, potentially causing state issues.
- With keys: Vue knows exactly which DOM element to remove.
Rendering Ranges
You can also use v-for
with integers to repeat an element a set number of times:
<template>
<div>
<h2>Counting to 5</h2>
<ul>
<li v-for="n in 5" :key="n">{{ n }}</li>
</ul>
</div>
</template>
Output:
- 1
- 2
- 3
- 4
- 5
Nested v-for
You can nest v-for
directives to iterate through multi-dimensional arrays:
<template>
<div>
<h2>Nested Lists</h2>
<ul>
<li v-for="(category, index) in nestedData" :key="index">
{{ category.name }}
<ul>
<li v-for="(item, itemIndex) in category.items" :key="itemIndex">
{{ item }}
</li>
</ul>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
nestedData: [
{ name: 'Fruits', items: ['Apple', 'Banana', 'Orange'] },
{ name: 'Vegetables', items: ['Carrot', 'Broccoli', 'Spinach'] }
]
}
}
}
</script>
Output:
- Fruits
- Apple
- Banana
- Orange
- Vegetables
- Carrot
- Broccoli
- Spinach
v-for
with a Component
You can use v-for
to render multiple instances of a component. This is useful for creating reusable UI elements:
<template>
<div>
<h2>User List</h2>
<user-card
v-for="user in users"
:key="user.id"
:name="user.name"
:role="user.role"
:email="user.email"
/>
</div>
</template>
<script>
import UserCard from './UserCard.vue'
export default {
components: {
UserCard
},
data() {
return {
users: [
{ id: 1, name: 'Alice', role: 'Developer', email: '[email protected]' },
{ id: 2, name: 'Bob', role: 'Designer', email: '[email protected]' },
{ id: 3, name: 'Charlie', role: 'Manager', email: '[email protected]' }
]
}
}
}
</script>
Filtering and Sorting Lists
You can use computed properties with v-for
to display filtered or sorted data:
<template>
<div>
<h2>Active Users</h2>
<ul>
<li v-for="user in activeUsers" :key="user.id">
{{ user.name }} - {{ user.status }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
users: [
{ id: 1, name: 'Alice', status: 'active' },
{ id: 2, name: 'Bob', status: 'inactive' },
{ id: 3, name: 'Charlie', status: 'active' },
{ id: 4, name: 'David', status: 'inactive' }
]
}
},
computed: {
activeUsers() {
return this.users.filter(user => user.status === 'active')
}
}
}
</script>
Output:
- Alice - active
- Charlie - active
Practical Example: Todo List
Here's a complete example of a simple todo list application using v-for
:
<template>
<div class="todo-app">
<h2>My Todo List</h2>
<div class="input-container">
<input
v-model="newTodo"
@keyup.enter="addTodo"
placeholder="Add a new task"
/>
<button @click="addTodo">Add</button>
</div>
<ul class="todo-list">
<li
v-for="(todo, index) in todos"
:key="todo.id"
:class="{ completed: todo.completed }"
>
<input
type="checkbox"
v-model="todo.completed"
/>
<span>{{ todo.text }}</span>
<button @click="removeTodo(index)" class="delete-btn">Delete</button>
</li>
</ul>
<div class="stats">
<p>{{ completedCount }} completed / {{ todos.length }} total</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
newTodo: '',
todos: [
{ id: 1, text: 'Learn Vue.js', completed: true },
{ id: 2, text: 'Build a todo app', completed: false },
{ id: 3, text: 'Master v-for directive', completed: false }
],
nextId: 4
}
},
computed: {
completedCount() {
return this.todos.filter(todo => todo.completed).length
}
},
methods: {
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({
id: this.nextId++,
text: this.newTodo,
completed: false
})
this.newTodo = ''
}
},
removeTodo(index) {
this.todos.splice(index, 1)
}
}
}
</script>
<style scoped>
.todo-app {
max-width: 500px;
margin: 0 auto;
}
.todo-list {
list-style-type: none;
padding: 0;
}
.todo-list li {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.completed {
text-decoration: line-through;
color: #999;
}
.delete-btn {
margin-left: auto;
color: red;
}
.input-container {
display: flex;
margin-bottom: 20px;
}
.input-container input {
flex-grow: 1;
padding: 8px;
margin-right: 10px;
}
</style>
Common Pitfalls and Best Practices
1. Array Mutation Methods
Vue can detect when reactive arrays are modified using the standard array mutation methods like push()
, pop()
, shift()
, unshift()
, splice()
, sort()
, and reverse()
.
However, there are cases where Vue cannot detect changes:
// This won't trigger a re-render
this.myArray[index] = newValue
// Instead, use Vue.set or Array.splice
this.$set(this.myArray, index, newValue)
// or
this.myArray.splice(index, 1, newValue)
2. Avoid using v-if
on the same element as v-for
When v-if
and v-for
are used together on the same element, v-for
takes priority. This can be inefficient if you're filtering items.
<!-- Avoid this -->
<li v-for="user in users" v-if="user.isActive" :key="user.id">
{{ user.name }}
</li>
<!-- Better approach -->
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
<!-- Or best, use a computed property -->
<li v-for="user in activeUsers" :key="user.id">
{{ user.name }}
</li>
Summary
The v-for
directive is a fundamental tool in Vue.js for rendering lists and working with collections of data. We've covered:
- Basic syntax and usage with arrays and objects
- The importance of providing a unique
:key
attribute - Accessing indexes and properties
- Nested list rendering
- Using
v-for
with components - Filtering and sorting data
- Best practices and potential pitfalls
When used correctly, v-for
allows you to create dynamic, efficient interfaces that respond to changing data.
Exercises
- Create a simple shopping list app that allows adding and removing items.
- Build a table with sortable columns using
v-for
and computed properties. - Create a component that displays a pagination system for a list of items (showing only a subset at a time).
Additional Resources
- Official Vue.js Guide on List Rendering
- Vue.js API Documentation for v-for
- Understanding Vue.js Reactivity
By mastering the v-for
directive, you'll be well-equipped to handle any list rendering needs in your Vue.js applications, from simple displays to complex, interactive data tables.
💡 Found a typo or mistake? Click "Edit this page" to suggest a correction. Your feedback is greatly appreciated!