In Vue 3, what kinds of values can a v-for directive iterate, and which aliases does each form give the template?
answer
- more than just arrays
- objects get three aliases
- a number counts from one
- Map entries arrive as pairs
basics
~10 sVue 3's v-for iterates arrays (item, index), plain objects (value, key, index), an integer n (1 to n, plus a 0-based index), strings, and any iterable such as Map or Set.
solid answer
~40 s`v-for` takes `alias in source` (or `of`). An **array** gives `(item, index)`; a **plain object** gives `(value, key, index)` in `Object.keys()` order; an **integer** `n` repeats the template with values `1..n` and a 0-based index; a **string** yields its characters; any **iterable** such as `Map` or `Set` is spread with `Array.from`, so a `Map` yields `[key, value]` pairs you can destructure. Aliases can be destructured like function parameters and are only visible inside the element. `<template v-for>` repeats several siblings without a wrapper, and on a component the item is **not** passed automatically: you bind it as a prop, with a `:key`.
code
vue · 21 lines<script setup lang="ts">
import { ref } from 'vue'
const users = ref([{ id: 1, name: 'Ada' }, { id: 2, name: 'Linus' }])
const profile = ref({ title: 'Engineer', city: 'Oslo' })
const roles = new Map([['ada', 'admin'], ['linus', 'viewer']])
</script>
<template>
<ul>
<li v-for="({ id, name }, index) in users" :key="id">{{ index }}: {{ name }}</li>
</ul>
<dl>
<template v-for="(value, key, index) in profile" :key="key">
<dt>{{ index }}. {{ key }}</dt>
<dd>{{ value }}</dd>
</template>
</dl>
<span v-for="n in 3" :key="n">{{ n }}</span>
<p v-for="[login, role] in roles" :key="login">{{ login }} is {{ role }}</p>
</template>go deeper
Recall the array and object alias shapes, that a number range starts at 1, and that <template v-for> repeats several siblings without a wrapper.
Explain that aliases behave like callback parameters: scoped, destructurable, not writable, and that a component needs the item passed as a prop.
Point out the ordering and typing traps: object order is property order, Map items are entry pairs, and v-model needs a member expression to write back.
Argue for normalising data into keyed arrays at the boundary so templates never depend on object order or ad hoc iteration forms.
## What v-for is `v-for` is Vue's built-in directive for rendering a block once per entry of a source value. Its expression has the special form `alias in source`, and Vue also accepts `alias of source` so it reads like a JavaScript `for...of`. The compiler turns the directive into a call to an internal list helper that walks the source and renders the element for every entry. What the aliases contain depends on the **type of the source**. ## The five source forms | Source | Syntax | Aliases | |---|---|---| | Array | `(item, index) in items` | element, 0-based position | | Plain object | `(value, key, index) in obj` | property value, property name, position | | Integer `n` | `(n, i) in 10` | `1..n`, 0-based position | | String | `(ch, i) in 'abc'` | each character, position | | Iterable (`Map`, `Set`, generators) | `([k, v], i) in map` | each iterated value, position | A few details interviewers like to check: - **Objects** are walked in the order `Object.keys()` returns, which is standard JavaScript property order (integer-like keys ascending, then string keys in insertion order), not a guaranteed business order. - **Ranges start at 1**, not 0: `v-for="n in 3"` renders 1, 2, 3. In development a negative or non-integer number logs a warning that the range expects a positive integer; `0` simply renders nothing. - **Map** is iterated as entries, so each item is a `[key, value]` pair. Destructure it in the alias. - **Destructuring** works exactly like function parameters: `v-for="({ id, name }, i) in users"`. ## Scope of the aliases The aliases behave like the parameters of a `forEach` callback: they exist only inside the element carrying `v-for` and its children, while everything from the component's scope stays readable. Nested `v-for` works like nested functions, so an inner loop can read the outer alias. Because an alias is a parameter, it is **not writable**. In an SFC build, `v-model="item"` on a bare alias is a compile error (`v-model cannot be used on v-for or v-slot scope variables because they are not writable.`). Bind to a property of the item (`v-model="item.name"`) or to the array slot (`v-model="items[index]"`) instead. ## Repeating more than one element To repeat a group of siblings with no wrapper element, put `v-for` on a `<template>` tag. The `<template>` itself renders nothing. In Vue 3 the `:key` belongs on the `<template>` tag; placing it on a child instead is reported by the compiler with `<template v-for> key should be placed on the <template> tag.` ## v-for on a component A component can carry `v-for` like any element, but the iterated item is **not injected** into it. Components have isolated scopes, so data must go in through props: 1. Put `v-for` and `:key` on the component tag. 2. Pass the item (and the index if needed) as props. 3. Declare those props in the child with `defineProps`. The docs give the reason: auto-injecting the item would couple the child to how it happens to be rendered, while explicit props keep it reusable elsewhere. ## Common mistakes - Expecting `n in 5` to start at 0. - Treating a `Map` item as the value rather than an entry pair. - Relying on object iteration for a meaningful order; sort an array instead. - Expecting the loop item to be visible inside a child component without a prop. - Writing to the alias with `v-model`. The `:key` on each form, and why it matters, is a separate subject: every example above that renders components or stateful elements should carry a stable, unique key.
- Why does `v-model="item"` inside a Vue 3 `v-for` fail to compile in an SFC?The alias is like a callback parameter: assigning to it would not change the array. The compiler rejects `v-model` on a bare `v-for` or `v-slot` scope variable. Bind to `item.name` when items are objects, or to `items[index]` when they are primitives, so the write reaches the reactive source.
- How would you render a Vue 3 `v-for` over an object in a specific order, say alphabetically by key?Do not rely on object iteration order, which follows `Object.keys()` property order. Build a computed array such as `Object.entries(obj).sort(([a], [b]) => a.localeCompare(b))` and iterate that with `([key, value]) in sortedEntries`, keyed by `key`.
saying these in an interview costs you the question
- A v-for range such as n in 5 starts at 0.
- The v-for item is automatically available inside a child component.
- v-for only accepts arrays, so a Map must be converted first.
- Object iteration in v-for follows a guaranteed business order.
- v-model can bind directly to the v-for alias of a primitive.