DEV Community

Nick Mousavi
Nick Mousavi

Posted on • Originally published at biomousavi.com

2 1 1 1 1

How to Access a Child Component’s Ref with multi-root node (Fragment) in Vue 3

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>

Enter fullscreen mode Exit fullscreen mode

That's very similar to Fragment in React. However, Vue handles fragments behind the scenes. In fact, in Vue 3, you can think of the <template> tag as a fragment.

The ref() Problem

In Vue 2, we could easily set a ref on a child component, and it would refer to both the wrapper element and the component instance.

But in Vue 3, when there’s no wrapper element, what does the ref refer to? 🤔

If the child component uses the Options API or doesn't use <script setup>, the ref will point to the child component's this, giving the parent full access to its properties and methods.

What if we use <script setup> ?

Components using <script setup> are private by default. To expose properties, we need to use the defineExpose macro.

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>  // [!code highlight]
})
</script>

<template>
  <Child ref="childRef" />
</template>
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode

Wait, what, what happened?

When we using Fragment(multiple nodes), Vue creates a text node that wraps our child component root nodes.

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 #text node.

#text is like a reference point that Vue uses internally.

Also I should mention that you have still access to component instance (if you don't use <script setup> in child).

Solution

1) Use Single Root Like this
2) 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>
Enter fullscreen mode Exit fullscreen mode

Now you have access to your refs and all the things that you exposed by using defineExpose.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay