Variable Components
Explore how to implement Vue's variable components using the <component> element and :is prop to dynamically render similar components. Understand when to apply this technique to reduce code duplication and when to prefer simple conditional rendering for clarity.
We'll cover the following...
Vue offers a lot of flexibility in templating. For example, v-if, v-else, and v-for allow us to switch between different components when necessary and help cover various use cases. Sometimes though, v-if adds a lot of overhead and leads to duplicate code. Variable components enable us to mitigate this issue.
What are variable components?
Vue offers a particular component simply called <component>. It technically provides all the features that every other component does with one addition—we can tell which actual component it should be. We do that with the :is prop. Since any HTML element is also a component, the most basic example of using a variable component is to make it behave like an HTML element.
The example above uses the default slot of <component> to render the inner text of a <p>. The example above would result in the generated HTML shown below:
ESLint requires us to add the prop via v-bind to eliminate possible bugs. We can pass any imported components via data props or computed values, as illustrated below:
Use cases and when to not use them
Variable components are convenient when switching out several very similar components. All possible components should derive from a common ancestor or use the same mixins. For example, these could be different layouts of the same data type, such as articles or commerce products. Another example could be compact list views or more extensive teaser views.
Let’s practice. The SPA below has two similar components to display articles. We want to display these articles in either a list or teaser view. Currently, we use v-if and v-else for them. Also, let’s try to abstract as many aspects as possible.
<template>
<ul>
<article-list-item
v-for="(article, key) in articles"
:key="key"
v-bind="article"
/>
</ul>
</template>
<script>
import ArticleListItem from './ArticleListItem.vue'
export default {
components: { ArticleListItem },
props: {
articles: {
type: Array,
required: true
}
}
}
</script>
<style scoped>
ul {
list-style-type: none;
padding: 0;
margin: 0;
}
</style>
See the hint below for a solution.
Sometimes we should prefer using v-if and v-else. Especially in cases where the components have very little in particular, abstracting things and trying to generalize usually creates unnecessary additional complexity.