# How Vue-Element-Admin Implements Dynamic Route Generation Based on User Roles

> Learn how vue-element-admin dynamically generates routes based on user roles in its permission.js router guard. See how async routes are filtered and added to Vue Router.

- Repository: [花裤衩/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- Tags: deep-dive
- Published: 2026-02-27

---

**Vue-element-admin generates dynamic routes at runtime by filtering asynchronous route trees against user roles retrieved from the backend, then injecting the permitted routes into Vue Router using `router.addRoutes()` within the [`src/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/permission.js) navigation guard.**

Vue-element-admin is a production-ready Vue.js admin dashboard that implements granular access control through runtime route generation. Instead of loading all navigation items upfront, the application constructs the sidebar menu dynamically based on the authenticated user's role set. This architecture relies on a permission-based routing system orchestrated by the router guard in [`src/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/permission.js) and the Vuex permission module in [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js).

## The Navigation Guard Flow in src/permission.js

The dynamic route generation process begins in [`src/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/permission.js) within the `router.beforeEach` navigation guard. This hook intercepts every route transition to verify authentication state and determine whether the user's permission set has been fetched and processed.

When a valid token exists (verified via `getToken()`) but the store lacks role information (`store.getters.roles` is empty), the guard initiates the permission flow. If no token is present, the user is redirected to the login page immediately.

## Fetching User Roles

Upon detecting an authenticated user without loaded permissions, the guard dispatches the `user/getInfo` action. This asynchronous call retrieves the current user's profile from the backend, including an array of role identifiers such as `['admin']` or `['editor']`.

```javascript
const { roles } = await store.dispatch('user/getInfo')   // src/permission.js L36-L38

```

The extracted `roles` array becomes the authority key for the subsequent route filtering process.

## Filtering Routes by Permission

The retrieved roles are passed to the `permission/generateRoutes` Vuex action defined in [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js). This module filters the application's asynchronous route tree to exclude inaccessible paths before they reach the Vue Router matcher.

```javascript
const accessRoutes = await store.dispatch('permission/generateRoutes', roles) // src/permission.js L40-L42

```

### Evaluating Route Permissions with hasPermission

Inside [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js), the `hasPermission` utility (lines 8-13) performs the atomic permission check. It determines whether a single route's `meta.roles` array intersects with the user's role list. Routes that do not define `meta.roles` are treated as public and always permitted.

```javascript
// src/store/modules/permission.js L8-L13
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

```

### Building the Accessible Route Tree

The `filterAsyncRoutes` function (lines 21-35) recursively traverses the entire async route configuration. It constructs a new array containing only routes where `hasPermission` returns true, effectively pruning entire navigation branches when parent routes are restricted.

```javascript
// src/store/modules/permission.js L21-L35
export function filterAsyncRoutes(routes, roles) {
  const res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}

```

### The Admin Role Bypass

The `generateRoutes` action (lines 49-61) implements a special case for administrators. If the user's role list contains `'admin'`, the function immediately grants access to **all** async routes without filtering. Otherwise, it invokes `filterAsyncRoutes` to compute the accessible subset, commits the result to the Vuex state via `SET_ROUTES`, and returns the filtered array.

```javascript
// src/store/modules/permission.js L49-L61
const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

```

## Dynamic Route Injection

After `generateRoutes` resolves, the guard receives the `accessRoutes` array and calls `router.addRoutes(accessRoutes)` (lines 42-44 in [`src/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/permission.js)). Because Vue Router's `addRoutes` method operates asynchronously, the guard triggers a final navigation with `next({ ...to, replace: true })` (lines 46-48) to ensure the newly injected routes are active for the current navigation request.

```javascript
router.addRoutes(accessRoutes)        // src/permission.js L42-L44
next({ ...to, replace: true })       // src/permission.js L46-L48

```

## Declaring Role Requirements in Route Config

Route definitions in [`src/router/index.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/router/index.js) specify required permissions using the `meta.roles` property. This declarative approach allows developers to restrict access to specific navigation items by listing authorized roles in an array.

```javascript
// src/router/index.js (asyncRoutes)
{
  path: '/permission',
  component: Layout,
  redirect: '/permission/page',
  alwaysShow: true,
  meta: { title: 'Permission', icon: 'lock', roles: ['admin', 'editor'] },
  children: [
    {
      path: 'page',
      component: () => import('@/views/permission/page'),
      meta: { title: 'Page Permission', roles: ['admin'] }
    }
  ]
}

```

Routes without a `roles` field remain accessible to all authenticated users, enabling a mixed public and secure navigation scheme.

## Runtime Route Regeneration

In scenarios where user permissions change without a full page reload—such as after a role promotion in an admin panel—the route set must be regenerated manually. This requires re-fetching user info, recomputing accessible routes through `generateRoutes`, and re-invoking `router.addRoutes()`.

```javascript
// Component logic after role change
await this.$store.dispatch('user/getInfo')
const { roles } = this.$store.getters
const accessRoutes = await this.$store.dispatch('permission/generateRoutes', roles)
this.$router.addRoutes(accessRoutes)

```

## Summary

- The [`src/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/permission.js) guard initiates **dynamic route generation** after login by verifying tokens and fetching user roles via `user/getInfo`.
- [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js) filters the route tree using `filterAsyncRoutes`, which relies on `hasPermission` to evaluate `meta.roles` metadata against the user's role array.
- **Administrators** bypass filtering entirely, receiving all async routes, while other roles receive a pruned subset based on the recursive permission check.
- `router.addRoutes()` injects the computed routes at runtime, followed by a replacement navigation (`next({ ...to, replace: true })`) to activate them immediately.
- Routes declare required roles through the `meta.roles` array in [`src/router/index.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/router/index.js), supporting both parent and child-level permission granularity.

## Frequently Asked Questions

### Where does vue-element-admin check if a user is allowed to access a specific route?

The permission check occurs in two places: the `hasPermission` function in [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js) evaluates `meta.roles` during the route generation phase, while the [`src/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/permission.js) guard prevents navigation to routes that haven't been generated yet by verifying the store's authentication and permission state before each route transition.

### What happens if a user has no roles assigned?

If the roles array is empty or the user lacks the specific roles listed in a route's `meta.roles`, `filterAsyncRoutes` removes those routes from the accessible set. The user will only see routes that omit the `roles` property entirely (public routes) or routes where `meta.roles` includes at least one of their assigned roles.

### How do I add a new protected page to vue-element-admin?

Add the route definition to the `asyncRoutes` array in [`src/router/index.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/router/index.js) and include a `meta: { roles: ['admin', 'yourRole'] }` property listing the authorized roles. The existing `generateRoutes` logic in [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js) will automatically include it for users possessing the specified roles when they next log in or refresh the page.

### Can the permission system work with custom logic instead of simple roles?

Yes. While the default implementation checks string role arrays, you can modify the `hasPermission` function in [`src/store/modules/permission.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/permission.js) to implement alternative schemes such as permission codes, bitwise flags, or function-based checks. The dynamic injection architecture via `generateRoutes` and `addRoutes` remains compatible with any permission evaluation logic you implement.