<div class="container">
  <h4>Vue.js Expandable Tree Menu<br/><small>(Recursive Components)</small></h4>
  <div id="app">
  <tree-menu 
             :nodes="tree.nodes" 
             :depth="0"   
             :label="tree.label"
             ></tree-menu>
  </div>
</div>


<script type="text/x-template" id="tree-menu">
  <div class="tree-menu">
    <div class="label-wrapper" @click="toggleChildren">
      <div :style="indent" :class="labelClasses">
        <i v-if="nodes" class="fa" :class="iconClasses"></i>
        {{ label }}
      </div>
    </div>
    <tree-menu 
      v-if="showChildren"
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label"
      :depth="depth + 1"   
    >
    </tree-menu>
  </div>
</script>
body {
  font-family: "Open Sans", sans-serif;
  font-size: 18px;
  font-weight: 300;
  line-height: 1em;
}

.container {
  width: 300px;
  margin: 0 auto;
}

.tree-menu {
  .label-wrapper {
    padding-bottom: 10px;
    margin-bottom: 10px;
    border-bottom: 1px solid #ccc;
    .has-children {
      cursor: pointer;
    }
  }
}
View Compiled
let tree = {
  label: 'root',
  nodes: [
    {
      label: 'item1',
      nodes: [
        {
          label: 'item1.1'
        },
        {
          label: 'item1.2',
          nodes: [
            {
              label: 'item1.2.1'
            }
          ]
        }
      ]
    }, 
    {
      label: 'item2'  
    }
  ]
}

Vue.component('tree-menu', { 
  template: '#tree-menu',
  props: [ 'nodes', 'label', 'depth' ],
  data() {
     return {
       showChildren: false
     }
  },
  computed: {
    iconClasses() {
      return {
        'fa-plus-square-o': !this.showChildren,
        'fa-minus-square-o': this.showChildren
      }
    },
    labelClasses() {
      return { 'has-children': this.nodes }
    },
    indent() {
      return { transform: `translate(${this.depth * 50}px)` }
    }
  },
  methods: {
    toggleChildren() {
       this.showChildren = !this.showChildren;
    }
  }
});

new Vue({
  el: '#app',
  data: {
    tree
  }
})
View Compiled

External CSS

  1. https://opensource.keycdn.com/fontawesome/4.7.0/font-awesome.min.css
  2. https://fonts.googleapis.com/css?family=Open+Sans:700,300

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js