Understanding ref and Template Refs in Vue 3 with Migration from Vue 2

Vue 3 introduces distinct concepts of ref for reactivity and template refs for accessing DOM elements or component instances.

ref: Reactive References

ref creates reactive data references in Composition API.

Basic Usage

import { ref } from 'vue'

const counter = ref(0)

console.log(counter.value) // Output: 0
counter.value++
console.log(counter.value) // Output: 1

Key Characteristics

  • Wraps primitive values: Converts basic types (numbers, strings, booelans) into reactive objects
  • Requires .value access: Must use .value to read or modify in JavaScript
  • Automatic unwrapping: Templates automatically unwrap refs without .value
<template>
  <div>{{ counter }}</div> <!-- No .value needed -->
</template>

Handling Complex Data

For objects and arrays, ref internally uses reactive:

const profile = ref({
  name: 'John Doe',
  age: 30
})

profile.value.age = 31

Template Refs: Accessing DOM and Components

Template refs provide direct access to DOM elements or component instances.

Vue 2 Implementation

In Vue 2, references were accesssed via this.$refs:

<template>
  <div>
    <input ref="inputField">
    <ChildComponent ref="childComp"/>
  </div>
</template>

<script>
export default {
  mounted() {
    this.$refs.inputField.focus()
    this.$refs.childComp.executeMethod()
  }
}
</script>

Vue 3 Composition API Aproach

Template refs usage changed in Vue 3:

  1. Declare ref variables
  2. Bind to elements/components
  3. Access via .value after mounting
<template>
  <div>
    <input ref="inputElement">
    <ChildComponent ref="childInstance"/>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const inputElement = ref(null)
const childInstance = ref(null)

onMounted(() => {
  inputElement.value.focus()
  childInstance.value.executeMethod()
})
</script>

Dynamic References in v-for

Use function-based refs in loops:

<template>
  <ul>
    <li 
      v-for="(fruit, idx) in fruitList" 
      :key="idx"
      :ref="element => { if (element) fruitRefs[idx] = element }"
    >
      {{ fruit }}
    </li>
  </ul>
</template>

<script setup>
import { ref, onBeforeUpdate } from 'vue'

const fruitList = ref(['Apple', 'Banana', 'Orange'])
const fruitRefs = ref([])

onBeforeUpdate(() => {
  fruitRefs.value = []
})
</script>

Migration Example: Vue 2 to Vue 3 with TypeScript

Consider migrating a complex Vue 2 component using dynamic refs in v-for:

Original Vue 2 template:

<template>
  <MixerControl 
    v-for="(item, index) in inputGroups" 
    :key="index"
    :ref="`input-${index}`"
  />
</template>

Vue 2 ref access pattern:

const refMap = {
  'input': `input-${channel}`
}

const refName = refMap[type]
const componentRefs = this.$refs[refName]

Vue 3 Migration Steps

  1. Declare reference arrays
const inputControlRefs = ref([])
  1. Bind in template
<MixerControl
  v-for="(item, idx) in inputGroups"
  :key="idx"
  :ref="element => { if (element) inputControlRefs.value[idx] = element }"
/>
  1. Access components
inputControlRefs.value[0].invokeMethod()

Key Differences and Migration Summary

  • ref: Creates reactive data, requires .value in JavaScript
  • Template refs: Access DOM/components
    • Vue 2: this.$refs collection
    • Vue 3: Declared ref variables bound in template
  • Migration approach:
    • Replace string refs with function-based bindings
    • Use ref arrays for v-for elements
    • Access via .value after component mount

This approach aligns with Vue 3's Composition API and improves TypeScript support.

Tags: Vue 3 Composition API Template Refs Vue Migration Reactivity

Posted on Thu, 30 Jul 2026 16:24:34 +0000 by tyson