
How to Access a Child Component’s Ref with multi-root node (Fragment) in Vue 3
In this article we want to explain how you can access to your child component's ref if you have more than one root (Fragment).
Introduction
If you're used to Vue 2, you might remember that every component's template needed a single root element. In Vue 3, that's no longer necessary because of fragments. This means your components can now have multiple root elements without needing a wrapper.
<!-- Vue 2 -->
<template>
<div> <!-- wrapper 😫 -->
<h1>My Blog Post</h1>
<ArticleComponent>{{ content }}</ArticleComponent>
</div>
</template>
<!-- Vue 3 -->
<template>
<h1>My Blog Post</h1>
<ArticleComponent>{{ content }}</ArticleComponent>
</template>
That's very similar to
The ref() Problem
In Vue 2, we could easily set a
But in Vue 3, when there’s no wrapper element, what does the
If the child component uses the
or doesn't useOptions API, the ref will point to the child component's this, giving the parent full access to its properties and methods.<script setup>
What if we use
Components using
are private by default. To expose properties, we need to use the<script setup>macro.defineExpose
Access To Children's Element
This is what happen when you have wrapper (single root) element:
<!-- Child -->
<template>
<div class="wrapper"> <!-- Root -->
<h1>My Blog Post</h1>
<ArticleComponent>{{ content }}</ArticleComponent>
</div>
</template>
<!-- Parent -->
<script setup lang="ts">
const childRef = ref()
onMounted(()=>{
console.log(childRef.value.$el); // <div class="wrapper">…</div>})
</script>
<template>
<Child ref="childRef" />
</template>
And when you have more than one root:
<!-- Child -->
<template>
<h1>My Blog Post</h1> <!-- Root 1 -->
<ArticleComponent>{{ content }}</ArticleComponent> <!-- Root 2 -->
</template>
<!-- Parent -->
<script setup lang="ts">
const childRef = ref()
onMounted(()=>{
console.log(childRef.value.$el); // #text})
</script>
<template>
<Child ref="childRef" />
</template>
Wait, what, what happened?
When we using Fragment(multiple nodes), Vue creates a
When using Fragments in Vue 3, Vue inserts an empty text node at the beginning of the component as a marker, which is why $el returns a
is like a reference point that Vue uses internally.#text
Also I should mention that you have still access to component instance (if you don't use
Solution
- Use Single Root Like this
- Use Template Refs + defineExpose
Using Template Refs + defineExpose
<!-- Child -->
<script setup lang="ts">
import { ref } from 'vue';
const h1Ref = ref()
const articleRef = ref()
defineExpose({
h1Ref,
articleRef
})
</script>
<template>
<h1 ref="h1Ref">My Blog Post</h1>
<ArticleComponent ref="articleRef">{{ content }}</ArticleComponent>
</template>
<!-- Parent -->
<script setup lang="ts">
const childRef = ref()
onMounted(()=>{
console.log(childRef.value); // {h1Ref: RefImpl, articleRef: RefImpl}
})
</script>
<template>
<Child ref="childRef" />
</template>
Now you have access to your
7 key questions and answers about template refs
A ref in Vue is a way to directly access DOM elements or child component instances. It provides a reference that you can use to manipulate elements, call methods, or access component data programmatically.
Vue 2 requires a single root element in every component (must wrap everything in one element), while Vue 3 supports fragments, allowing multiple root nodes without a wrapper element.
In Vue 2, refs are accessed via
- Declare the ref using const myRef = ref(null)
- Access it using myRef.value
- Use if you want to make refs available to parent components.defineExpose
4. When using
In Vue 2,
5. When using
When using
6. What are two main approaches to handle refs in Vue 3 when you need parent access to child elements?
- Use a single root element if you just need simple DOM access
- Use template refs with defineExpose when you need access to specific elements, making them explicitly available to the parent component
7. What's the difference between using
Both can be used for template references, but they have different approaches and benefits:
- Using ref (before Vue 3.5):
<script setup>
const input = ref(null) // name must match template ref
</script>
<template>
<input ref="input" />
</template>
- Using useTemplateRef (Vue 3.5+):
<script setup>
const input = useTemplateRef('my-input') // first argument matches ref value
</script>
<template>
<input ref="my-input" />
</template>
Key differences:
- useTemplateRef provides better TypeScript inference - it automatically infers the type of the element/component
- With ref, the variable name must match the template ref value
- With useTemplateRef, you explicitly specify the ref name as the first argument, allowing different variable and ref names
- Both approaches only provide access to the element after component mounting
- Both will be null before mounting or when element is unmounted (e.g., by v-if)