Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/template-ref-v-for-order.md
1---2title: Template Ref Array Order Not Guaranteed in v-for3impact: MEDIUM4impactDescription: Refs collected from v-for may not match source array order, causing index-based bugs5type: gotcha6tags: [vue3, template-refs, v-for, arrays, ordering]7---89# Template Ref Array Order Not Guaranteed in v-for1011**Impact: MEDIUM** - When using template refs inside `v-for`, Vue collects the element references into an array. However, this array does NOT guarantee the same order as the source array. Relying on index-based access can lead to subtle bugs.1213This caveat is not obvious and can cause hard-to-debug issues when you assume the ref array matches your data order.1415> **Warning: `useTemplateRef()` does NOT work with v-for refs in Vue 3.5**16>17> The `useTemplateRef()` API returns `null` when used with refs inside `v-for`. This is a known limitation. You must use the legacy pattern with `ref()` and a matching template ref name:18>19> ```ts20> // Does NOT work with v-for - returns null21> const itemRefs = useTemplateRef('items')22>23> // Works with v-for - use this pattern instead24> const items = ref([]) // name must match ref="items" in template25> ```26>27> The examples in this rule show `useTemplateRef()` for illustration, but in practice you should use the legacy `ref()` pattern for v-for scenarios until this limitation is addressed.2829## Task Checklist3031- [ ] Never assume ref array indices match source data array indices32- [ ] Use data attributes or other identifiers to correlate refs with data33- [ ] Consider function refs for complex scenarios requiring ordered access34- [ ] Test with dynamic list operations (add, remove, reorder) to verify behavior3536**Incorrect:**37```vue38<script setup>39import { ref, useTemplateRef, onMounted } from 'vue'4041const items = ref(['First', 'Second', 'Third'])42const itemRefs = useTemplateRef('items')4344onMounted(() => {45// WRONG: Assuming itemRefs[0] corresponds to items[0]46// The order is NOT guaranteed to match!47items.value.forEach((item, index) => {48console.log(`${item}: `, itemRefs.value[index]) // May be wrong element!49})50})51</script>5253<template>54<ul>55<li v-for="item in items" ref="items" :key="item">56{{ item }}57</li>58</ul>59</template>60```6162**Correct:**63```vue64<script setup>65import { ref, useTemplateRef, onMounted } from 'vue'6667const items = ref([68{ id: 1, text: 'First' },69{ id: 2, text: 'Second' },70{ id: 3, text: 'Third' }71])72const itemRefs = useTemplateRef('items')7374onMounted(() => {75// CORRECT: Use data attributes to identify elements76itemRefs.value.forEach(el => {77const id = el.dataset.id78const item = items.value.find(i => i.id === Number(id))79console.log(`${item.text}: `, el)80})81})82</script>8384<template>85<ul>86<li87v-for="item in items"88ref="items"89:key="item.id"90:data-id="item.id"91>92{{ item.text }}93</li>94</ul>95</template>96```9798```vue99<script setup>100import { ref, onMounted, onBeforeUpdate } from 'vue'101102const items = ref(['First', 'Second', 'Third'])103const itemRefs = ref(new Map())104105// CORRECT: Use function refs for precise control106function setItemRef(el, item) {107if (el) {108itemRefs.value.set(item, el)109} else {110itemRefs.value.delete(item)111}112}113114// Reset before each update to handle removed items115onBeforeUpdate(() => {116itemRefs.value.clear()117})118119onMounted(() => {120// Access refs by their associated data item121items.value.forEach(item => {122const el = itemRefs.value.get(item)123console.log(`${item}: `, el)124})125})126</script>127128<template>129<ul>130<li131v-for="item in items"132:key="item"133:ref="(el) => setItemRef(el, item)"134>135{{ item }}136</li>137</ul>138</template>139```140141```vue142<script setup>143import { ref, useTemplateRef, onMounted } from 'vue'144145const items = ref(['First', 'Second', 'Third'])146const itemRefs = useTemplateRef('items')147148// CORRECT: If order matters, sort refs by DOM position149onMounted(() => {150const sortedRefs = [...itemRefs.value].sort((a, b) => {151// Sort by DOM order using compareDocumentPosition152return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1153})154155// Now sortedRefs matches visual/DOM order156sortedRefs.forEach((el, index) => {157console.log(`Position ${index}: `, el.textContent)158})159})160</script>161162<template>163<ul>164<li v-for="item in items" ref="items" :key="item">165{{ item }}166</li>167</ul>168</template>169```170171## Reference172- [Vue.js Template Refs - Refs inside v-for](https://vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for)173