see also Vue Ref Object

In cases where we need direct access to the DOM elements we can use the ref attribute that allows us to get a direct reference to a specific element.

<input ref="input">

To obtain the reference declare a ref that matches the attribute name

<script setup>
import { ref, onMounted } from 'vue'
 
// declare a ref to hold the element reference
// the name must match template ref value
const input = ref(null)
 
onMounted(() => {
  input.value.focus()
})
</script>
 
<template>
  <input ref="input" />
</template>

If watching the changes of a ref in a template expression make sure to account for the null value, since the element only exists after the first render.

watchEffect(() => {
  if (input.value) {
    input.value.focus()
  } else {
    // not mounted yet, or the element was unmounted (e.g. by v-if)
  }
})

v-for

To access a ref attribute from a v-for directive the script ref should contain an Array value.
The ref array does not guarantee the same order as the source array.

<script setup>
import { ref, onMounted } from 'vue'
 
const list = ref([
  /* ... */
])
 
const itemRefs = ref([])
 
onMounted(() => console.log(itemRefs.value))
</script>
 
<template>
  <ul>
    <li v-for="item in list" ref="itemRefs">
      {{ item }}
    </li>
  </ul>
</template>

Function Refs

Instead of using a string key, the ref attribute can also be bound to a function, which will be called on each component update. The function receives the element reference as first argument.

<input :ref="(el) => { /* assign el to a property or ref */ }">

Here a dynamic :ref binding is used. When the element is unmounted the value will be null. A method can also be used instead of an inline function

Component Refs

The attribute can also be used on a child component. The reference in this case will be of component instance.
When using <script setup> the component is private by default, so the reference won’t be able to access anything unless the child component exposes a public interface using defineExpose

<script setup>
import { ref } from 'vue'
 
const a = 1
const b = ref(2)
 
// Compiler macros, such as defineExpose, don't need to be imported
defineExpose({
  a,
  b
})
</script>

The instance of this component via template refs will be { a:number, b:number} as refs are automatically unwrapped