Skip to content

v-for and List Rendering

v-for renders a list of elements from an array or object. It is how FamilyTree renders every list: all parents, all children, all former spouses, the people sidebar.

<script setup lang="ts">
const people = [
{ id: '1', name: 'Alice' },
{ id: '2', name: 'Bob' },
{ id: '3', name: 'Carol' },
]
</script>
<template>
<ul>
<li v-for="person in people" :key="person.id">
{{ person.name }}
</li>
</ul>
</template>

The syntax is v-for="item in array". Each iteration has access to item.

:key is required with v-for. It must be a unique, stable identifier — typically the item’s id. Vue uses it to track which list items correspond to which DOM elements when the array changes.

Without :key, Vue may re-use the wrong DOM element when items are added, removed, or reordered — causing subtle bugs with input state, animations, or transitions.

<!-- ✅ Correct — stable unique key -->
<PersonCard v-for="p in people" :key="p.id" :person="p" />
<!-- ❌ Avoid — index as key causes issues when list changes -->
<PersonCard v-for="(p, index) in people" :key="index" :person="p" />

Use the array index as the key only when the list is completely static and never reordered.

The second parameter gives the current index:

<li v-for="(person, index) in people" :key="person.id">
{{ index + 1 }}. {{ person.name }}
</li>

v-for also works on objects:

<li v-for="(value, key) in person" :key="key">
{{ key }}: {{ value }}
</li>

Use v-for with components the same way as with elements:

<PersonCard
v-for="person in parents"
:key="person.id"
:person="person"
role="parent"
:clickable="true"
@select="focusOn"
/>

Each PersonCard instance gets its own set of props. When parents changes, Vue reconciles the list using the :key values.

Avoid using v-if and v-for on the same element — Vue 3 evaluates v-if first, which means the loop variable isn’t available in the condition.

Instead, wrap with a <template> or filter the array in a computed:

<!-- ✅ Preferred — filter in computed -->
<PersonCard
v-for="p in activeParents"
:key="p.id"
:person="p"
/>
<!-- ❌ Avoid — v-if and v-for on same element -->
<PersonCard
v-for="p in people"
v-if="p.isParent"
:key="p.id"
/>
  1. Create a component with a ref<{ id: string; name: string }[]> array.
  2. Render each item using v-for with a PersonCard component, passing name as a prop.
  3. Add a button that pushes a new person to the array. Verify the list updates.
  4. Add a button next to each person that removes them from the array using .filter().
  • v-for="item in array" renders an element for each item.
  • :key is required — use a stable unique identifier, not the array index.
  • Access the index as the second parameter: v-for="(item, index) in array".
  • Combine v-for with components exactly as with HTML elements.
  • Filter arrays in a computed rather than using v-if on the same element as v-for.