DEV Community

Cover image for Building Recursive components in Vue
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

20 3 2 4 2

Building Recursive components in Vue

Few days ago, I wanted to build a Vue component that would be able to display a nested node tree where we don't know the exact nesting number - so it would need to be able to work recursively. I was surprised about the fact that there are not too many resources in the web about it so I decided to write an article about it.

The main idea is to have one component that will accept data property (with unknown data structure for now) and two other properties for summary an open; first used for defining the name used in the details tag and the displayed text, and the second used to see if the node tree is open or not.

I hope you will like it :)

Enjoy!

🟢 Building Recursive components in Vue

In this example, I am using VueUse onClickOutside utility function to handle outside click action (I just love this utility!).

First, let's define the props that this component will accept:

const props = defineProps({
  summary: {
    type: String,
    default: 'disclosure'
  },
  open: {
    type: Boolean,
    default: false
  },
  content: {
    type: Object,
    default: () => ({})
  }
})
Enter fullscreen mode Exit fullscreen mode

Next, let's define the reactive ref and computed variables:

const computedOpenState = ref(props.open)
const details = ref(null)
const hasNestedChildren = computed(() => props.content.children?.length)
Enter fullscreen mode Exit fullscreen mode

And finally, last in the script tag, let's add function to handle both toggle details and outside click:

const toggleDetails = (event: ToggleEvent) => {
  computedOpenState.value = event.newState === 'open'
}

onClickOutside(details, () => (computedOpenState.value = false))
Enter fullscreen mode Exit fullscreen mode

Now, let's add a bit of template to our component. First if the nested content does not have more nested children, we just want to display a paragraph with the name of the summary:

  <p v-if="!hasNestedChildren">
    {{ summary }}
  </p>
Enter fullscreen mode Exit fullscreen mode

In other case, we want to utilise the details HTML tag:

<p v-if="!hasNestedChildren">
    {{ summary }}
  </p>
  <details
    v-else
    :open="computedOpenState"
    :name="summary"
    ref="details"
    @toggle="toggleDetails"
  >
   ...
  </details>
Enter fullscreen mode Exit fullscreen mode

Inside the details tag, we want to add following code to handle both displaying the summary and nested node tree:

    <summary>
      {{ summary }}
    </summary>
    <template v-if="hasNestedChildren">
      <NodeTree
        v-for="(child, key) in content.children"
        :key
        :summary="child.summary"
        :content="child"
        @click="toggleChildren"
      />
    </template>
Enter fullscreen mode Exit fullscreen mode

Below, you can see full code of this component:

<script setup lang="ts">
import { ref, computed } from 'vue' 
import { onClickOutside } from '@vueuse/core'

const props = defineProps({
  summary: {
    type: String,
    default: 'disclosure'
  },
  open: {
    type: Boolean,
    default: false
  },
  content: {
    type: Object,
    default: () => ({})
  }
})

const computedOpenState = ref(props.open)
const details = ref(null)
const hasNestedChildren = computed(() => props.content.children?.length)

const toggleDetails = (event: ToggleEvent) => {
  computedOpenState.value = event.newState === 'open'
}

onClickOutside(details, () => (computedOpenState.value = false))
</script>

<template>
  <p v-if="!hasNestedChildren">
    {{ summary }}
  </p>
  <details
    v-else
    :open="computedOpenState"
    :name="summary"
    ref="details"
    @toggle="toggleDetails"
  >
    <summary>
      {{ summary }}
    </summary>
    <template v-if="hasNestedChildren">
      <NodeTree
        v-for="(child, key) in content.children"
        :key
        :summary="child.summary"
        :content="child"
        @click="toggleChildren"
      />
    </template>
  </details>
</template>
Enter fullscreen mode Exit fullscreen mode

Finally, the component can be used like following:

<template>
  <NodeTree v-if="data" :content="data" :summary="data.summary" />
</template>
Enter fullscreen mode Exit fullscreen mode

And that's it! You have fully working recursive node tree component :)

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

✅ Summary

Well done! You have just learned how to build a recursive Vue component.

Take care and see you next time!

And happy coding as always 🖥️

$150K MiniMax AI Agent Challenge — Build Smarter, Remix Bolder, Win Bigger!

Join the MiniMax AI Agent Challenge — Build your first AI Agent 🤖

Developers, innovators, and AI tinkerers, build your AI Agent and win $150,000 in cash. 💰

Read more →

Top comments (0)

Short-term memory for faster AI agents

Short-term memory for faster AI agents

AI agents struggle with latency and context switching. Redis fixes it with a fast, in-memory layer for short-term context—plus native support for vectors and semi-structured data to keep real-time workflows on track.

Start building

👋 Kindness is contagious

Discover fresh viewpoints in this insightful post, supported by our vibrant DEV Community. Every developer’s experience matters—add your thoughts and help us grow together.

A simple “thank you” can uplift the author and spark new discussions—leave yours below!

On DEV, knowledge-sharing connects us and drives innovation. Found this useful? A quick note of appreciation makes a real impact.

Okay