We've all created a web app using Vue and for sure these apps have a menu in it.

The main idea came to life when I was developing a side project and I had to create too many routes and child routes too. So, I decided to create this tutorial to show you how I solved my problem.


Let's get started

First thing first, create a project with Vue CLI

bash vue create dynamic-menu-vue

bash Vue CLI v3.11.0 ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, Router, CSS Pre-processors, Linter ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass) ? Pick a linter / formatter config: Basic ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json ? Save this as a preset for future projects? No

Once your project is ready, you can serve it as normal

bash npm run serve -- --open

View source code

View live demo


Let's break down the code I wrote for src/router.js

```js import Vue from "vue"; import Router from "vue-router";

Vue.use(Router);

export default new Router({ mode: "history", base: "/dynamic-menu-vue/", routes: [ { path: "/", redirect: { path: "/home" }, meta: { visible: false } }, { path: "/home", name: "home", component: () => import(/ webpackChunkName: "home" / "./views/Home.vue"), meta: { visible: true }, children: [ { path: "sub-view-1", name: "sub-view-1", component: () => import( / webpackChunkName: "home-sub-view-1" / "./components/Home/SubView1.vue" ), meta: { visible: true } }, { path: "sub-view-2", name: "sub-view-2", component: () => import( / webpackChunkName: "home-sub-view-2" / "./components/Home/SubView2.vue" ), meta: { visible: true } } ] }, { path: "/about", name: "about", component: () => import(/ webpackChunkName: "about" / "./views/About.vue"), meta: { visible: true } }, { path: "", name: "not-found", component: () => import(/ webpackChunkName: "not-found" */ "./views/NotFound.vue"), meta: { visible: false
} } ] }); ```

  • meta: This attribute is used to enhance routes. In this situation, we just use it to make routes visible on the menu,
  • lazy loading: there is no actual need to use lazy loading at this project but it's a good trick to downsize the bundle size,
  • base: I set this base URL in order to deploy this project on GitHub Pages.

OK, now that we have routes we need to create the menu component. I'm going to use bootstrap for this one. Let's add bootstrap to the project.

bash npm install bootstrap --save

Then create a new file named styles.scss under the src folder and add these lines of code

scss @import "./assets/variables"; @import "node_modules/bootstrap/scss/bootstrap.scss"; @import "./assets/bootswatch";

Now, add styles.scss in main.js

js ... import "./styles.scss"; ...


Menu Component

Under the src/components create a new folder named Menu and create two new files in it.

  1. Navbar.vue
  2. MenuItem.vue

Let's continue with Navbar.vue

```vue

```

The computed property routes() returns the content of the router.js file.

Then the MenuItem.vue

```vue

```

Properties

  • route the exact route that comes from parent element Navbar.vue

Computed properties

  • isVisible() returns true if a route should be on the menu, false otherwise
  • name() returns the route's name

Active route class

  • exact-active-class="text-success" this, adds a class to the active menu item.

As you can notice we have a recursive call to the same component if the route has child component.


That's all for now. If you have any questions let me know, I'll be glad to help.

Happy coding!


If you found this post helpful or enjoyed it, consider supporting me by buying me a coffee. Your support helps me create more valuable content. ☕ Thank you!