In one of my earlier posts, I briefly mentioned the concept of nested component rendering in Vue. Despite this technique's usefulness, there aren't many clear examples online showing how and when to use it effectively.
So let's fix that.
The problem
Let's say we're building a dynamic table. The table has rows and columns, and each cell needs to render different content intelligently depending on its data type.
For example:
- If the value is a date, it should format it.
- If it's a number and marked as a "price", it should be formatted as currency.
- If it's a boolean, it should display "yes" or "no".
- If it's a nested array of cells, it should recursively render a set of cells inside itself.
We want a single, reusable TableCell
component that can handle all these cases on its own.
The goal
Our goal is to create a flexible <TableCell />
component that receives a type
and a value
as props and renders the content accordinglyβββeven recursively, if needed.
The implementation
We'll use Vue 3's <script setup>
syntax and TypeScript for better type safety.
Here's a simplified version of what our component might look like:
<template>
<div
:class="`_${data.type}`"
class="tableCell"
>
...
<template v-if="data.type === TypesEnum.Boolean">
{{ data.value ? 'yes' : 'no' }}
</template>
<template v-else-if="data.type === TypesEnum.Primitive">
{{ data.value ?? '-' }}
</template>
<template v-else-if="data.type === TypesEnum.Array">
<!-- Recursive call -->
<TableCell
v-for="(item, key) in data.value"
:key="key"
:data="item"
/>
</template>
</div>
</template>
<script lang="ts" setup>
import { type Component } from 'vue';
/** Important to ensure the component knows where to get component from. */
defineOptions({
name: 'TableCell',
});
enum TypesEnum {
Array = 'array',
Boolean = 'boolean',
Component = 'component',
Date = 'date',
Icon = 'icon',
Price = 'price',
Primitive = 'primitive',
}
type PrimitiveValue =
| { type: TypesEnum.Boolean; value?: boolean }
| { type: TypesEnum.Component; value: Component }
| { type: TypesEnum.Date; value: Date }
| { type: TypesEnum.Icon; value: Component }
| { type: TypesEnum.Price; value: number }
| { type: TypesEnum.Primitive; value?: boolean | number | string };
type ComplexValue = {
type: TypesEnum.Array;
value: PrimitiveValue[];
};
defineProps<{
data: ComplexValue | PrimitiveValue;
}>();
</script>
Comments
Parts responsible for formatting were intentionally left out of the example to keep it simple. However, some basic ones were included to give a more complete picture.
Itβs also important to ensure the component knows where to get its data from.
When to use it
Recursive components are ideal when:
- You're dealing with tree-like or nested data.
- You want to minimize repeated boilerplate logic for rendering similar structures.
- You need consistent behavior and formatting rules for the same data type, even when nested.
Just be mindful of recursion limits, performance, and base case fallbacks.
π§ Final thought
I hope you found this useful and maybe even a bit fun to explore.
If it sparked your interestβββstay tuned and consider subscribing to my blog!
See you in the commentsβββand thanks for reading! π
Top comments (0)