Use Vue 3's Fragment feature in Vue 2 to return multiple root elements
<template>
<div v-frag> ⬅ This root element is unwrapped and removed on render!
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
</div>
</template>
👉 Try out a demo in this CodePen!
Support this project by ⭐️ starring and sharing it. Follow me to see what other cool projects I'm working on! ❤️
- ✅ Multiple root nodes Without creating a functional component!
- 🔥 SSR Unwraps the root element on client-side post-hydration!
- ⚡️ Directives Supports
v-if
,v-for
, andv-html
! - 👩🔬 Battle-tested Checkout the tests here!
npm i vue-frag
Make it available anywhere in your Vue application.
import frag from 'vue-frag';
Vue.directive('frag', frag);
Explicitly register it to a component you want to use it in.
...
<script>
import frag from 'vue-frag';
export default {
directives: {
frag
},
...
};
</script>
Create a Fragment.vue
component:
<template>
<div v-frag>
<slot />
</div>
</template>
<script>
import frag from 'vue-frag';
export default {
directives: {
frag
}
};
</script>
And use it as a component:
<template>
<fragment>
No root element!
</fragment>
</template>
<template>
<div v-frag> <!-- This element will be unwrapped -->
<div v-for="i in 10">
{{ i }}
</div>
</div>
</template>
<template>
<div>
<!-- Unwraps the root node of some-custom-component -->
<some-custom-component v-frag />
</div>
</template>
<template>
<div v-frag>
<template v-if />
</div>
</template>
<template>
<div v-frag>
Hello world
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$el.frag)
}
}
</script>
Whenever you feel like the root-element of your component adds no value and is unnecessary, or is messing up your HTML output. This usually happens when you want to return a list of elements like <li>List Items</li>
or <tr><td>Table Rows</td></tr>
but you have to wrap it in a <div>
.
In Vue 2, it's possible to return multiple nodes with a Functional Component but functional components are stateless (no data()
or life-cycle hooks), doesn't support methods
, doesn't have very good template support, and can lead to SSR bugs (eg. mismatching nodes).
Related VueJS Issues / Stackoverflow Qs:
How is this different from vue-fragment?
They are both designed to do the same thing. However, vue-fragment is a component and vue-frag is a directive. I made vue-frag when I saw vue-fragment didn't have any tests to ensure correct behavior, had a lot of unattended issues, and didn't seem actively maintained. In terms of size, they are both small but vue-frag is slightly smaller (993B
vs 798B
).
Vue associates vNodes with specific DOM references so once a component has mounted, the DOM nodes can be moved around and Vue will still be able to mutate them by reference. The Frag directive simply replaces the root element of a component in the DOM with it's children upon DOM insertion, and monkey-patches native properties like parentNode
on the children to make Vue think they're still using the component root element.
Unfortunately not. v-show
works by setting style="display: none"
on the root element of the target component, and with vue-frag
unwrapping and removing the root element, there would be no grouping-element to apply the display: none
to. If the fragment returned elements, it's possible to apply it to each child-node, but it's possible for them to be text-nodes which cannot be styled.
- vue-subslot - 💍 pick out specific elements from component
<slot>
s - vue-vnode-syringe - 🧬 Add attributes and event-listeners to
<slot>
content 💉 - vue-proxi - 💠 Tiny proxy component
- vue-pseudo-window - 🖼 Declaratively interface window/document in your Vue template
- vue-v - render vNodes via component template