While Vue handles most DOM updates declaratively, some tasks require direct access to an underlying element or child component. Common examples include focusing an input, playing a <video>, drawing on <canvas>, or integrating a charting library.
In the Composition API, template refs give you a direct reference to the mounted DOM element or child component instance once the component renders.
flowchart LR
Decl["const inputEl = ref(null)"] --> Attr["input ref='inputEl'"]
Attr --> Mount["onMounted()"]
Mount --> Call["inputEl.value.focus()"]
<script setup>Create a ref(null) whose variable name matches the ref="inputEl" attribute in <template>. In Vue 3.5+, you can also use the dedicated useTemplateRef('inputEl') helper for clearer naming.
Remember that <script setup> components are private by default: a parent ref cannot call child functions unless the child exposes them explicitly with defineExpose().
defineExpose ExampleFirst, expose resetChildInput from CustomInput.vue using defineExpose().
<script setup>
import { ref } from 'vue';
const childText = ref('');
function resetChildInput() {
childText.value = '';
}
defineExpose({
resetChildInput
});
</script>
<template>
<input v-model="childText" placeholder="Child input field" />
</template>
Next, focus the native input in onMounted() and call the exposed child method from the parent component.
<script setup>
import { ref, onMounted } from 'vue';
import CustomInput from './CustomInput.vue';
const inputElement = ref(null);
const childCompRef = ref(null);
onMounted(() => {
if (inputElement.value) {
inputElement.value.focus();
}
});
function callChildMethod() {
childCompRef.value?.resetChildInput();
}
</script>
<template>
<div class="ref-container">
<label>
Auto-Focused Input:
<input ref="inputElement" type="text" placeholder="Focused on mount" />
</label>
<CustomInput ref="childCompRef" />
<button @click="callChildMethod">Reset Child Input</button>
</div>
</template>
inputElement.value is null. Always access template refs inside onMounted(), event handlers, or watchers with { flush: 'post' }.defineExpose() on <script setup> Children: Without defineExpose(), childCompRef.value is an empty object and cannot access child functions or refs.v-for: When putting ref="itemRefs" on a v-for element, itemRefs.value becomes an array of DOM nodes, which is not guaranteed to keep the exact source array order after mutations.Use props and emitted events for normal data flow. Reserve template refs for imperative browser APIs like .focus(), .scrollIntoView(), .select(), or third-party DOM libraries.
v-if?Yes. Use watchEffect() or watch(inputElement, (el) => { if (el) el.focus(); }) to react whenever a conditional element mounts or unmounts.
useTemplateRef() in Vue 3.5+?useTemplateRef('inputElement') separates template ref declarations from reactive state ref() calls, making code easier to read and type in TypeScript.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With