DEV Community

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

Posted on

19 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 🖥️

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay