# How to Add a New Route in Celeris Web Using Vue Router

> Learn how to add a new route in Celeris Web using Vue Router. Create a TypeScript module and export RouteRecordRaw for automatic registration and permission control.

- Repository: [Kirk Lin/celeris-web](https://github.com/kirklin/celeris-web)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Developers add new routes in Celeris Web by creating a TypeScript module under `apps/admin/src/router/routes/modules/` that exports a `RouteRecordRaw` object, which is automatically glob-imported and registered via the permission guard.**

Celeris Web, the Vue 3 admin framework in the `kirklin/celeris-web` repository, uses a file-based routing convention that eliminates manual route registration. Instead of editing a central router configuration, you create declarative route modules that the build system discovers automatically.

## Understanding the Route Architecture

Celeris Web separates routes into two categories to handle authentication and code splitting efficiently.

**`basicRoutes`** are static routes always available at startup. These include the login page, root redirects, and 404 handlers defined in [`apps/admin/src/router/routes/index.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/routes/index.ts).

**`asyncRoutes`** are dynamically loaded from the `modules/` directory. These routes are discovered at build time using `import.meta.glob`, processed by the `loadRoutesFromModules` utility, and registered only after the user authenticates via the permission guard in [`apps/admin/src/router/guard/permissionGuard.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/guard/permissionGuard.ts).

## Creating a New Route Module

To add a new route, create a TypeScript file in the modules directory that exports a default `RouteRecordRaw` configuration.

### Step 1: Create the Route File

Create a new file at `apps/admin/src/router/routes/modules/[feature-name].ts`. For example, to add a reports section:

```typescript
import type { RouteRecordRaw } from "vue-router";
import { LAYOUT } from "~/router/constant";

const reports: RouteRecordRaw = {
  path: "/reports",
  name: "ReportsRoot",
  component: LAYOUT,
  redirect: "/reports/summary",
  meta: {
    title: "routes.reports.title",
    icon: "i-mdi-file-chart",
    orderNumber: 100,
  },
  children: [
    {
      path: "summary",
      name: "ReportsSummary",
      component: () => import("~/pages/reports/summary.vue"),
      meta: {
        title: "routes.reports.summary",
        icon: "i-mdi-chart-pie",
      },
    },
    {
      path: "detail/:id",
      name: "ReportDetail",
      component: () => import("~/pages/reports/detail.vue"),
      meta: {
        title: "routes.reports.detail",
        shouldHideInMenu: true,
      },
    },
  ],
};

export default reports;

```

### Step 2: Define Meta Properties for the Menu

The `meta` object controls how the route appears in the navigation sidebar managed by [`packages/web/utils/src/menuHelper.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/menuHelper.ts):

- **`title`**: The display label (typically an i18n key).
- **`icon`**: The icon class for the sidebar.
- **`orderNumber`**: Controls the sort order in the menu.
- **`shouldHideInMenu`**: Set to `true` to exclude the route from the sidebar.
- **`shouldAffixToNavBar`**: Pins the route to the top navigation bar.

## How Automatic Route Discovery Works

The routing system uses Vite's `import.meta.glob` to collect all route modules without manual imports.

In [`apps/admin/src/router/routes/index.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/routes/index.ts), the system executes:

```typescript
const modules = import.meta.glob("./modules/**/*.ts", { eager: true });

```

The `loadRoutesFromModules` function in [`packages/web/utils/src/router.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/router.ts) then transforms this glob map into a clean array of `RouteRecordRaw` objects. This design means any new file placed under `apps/admin/src/router/routes/modules/` (including subdirectories) is instantly part of the `asyncRoutes` collection at build time.

## Registering Routes at Runtime

While file-based routes cover most use cases, you can also register routes dynamically after the application starts.

The permission guard in [`apps/admin/src/router/guard/permissionGuard.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/guard/permissionGuard.ts) demonstrates the standard pattern for adding async routes after authentication:

```typescript
asyncRoutes.forEach((route) => {
  router.addRoute(route);
});

```

For user-specific routes fetched from an API, the user store in [`apps/admin/src/store/modules/user.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/store/modules/user.ts) shows how to add routes on the fly:

```typescript
import { router } from "~/router";

const dynamicRoute: RouteRecordRaw = {
  path: "/custom/:slug",
  name: "CustomPage",
  component: () => import("~/pages/custom/index.vue"),
  meta: { requiresAuth: true },
};

router.addRoute(dynamicRoute);

```

## Key Files Reference

| File | Purpose |
|------|---------|
| [`apps/admin/src/router/routes/index.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/routes/index.ts) | Defines `basicRoutes` and loads all module routes via glob import. |
| `apps/admin/src/router/routes/modules/*.ts` | Individual route definitions (e.g., [`dashboard.ts`](https://github.com/kirklin/celeris-web/blob/main/dashboard.ts)). |
| [`packages/web/utils/src/router.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/router.ts) | Contains `loadRoutesFromModules` for processing globbed routes. |
| [`apps/admin/src/router/guard/permissionGuard.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/guard/permissionGuard.ts) | Registers `asyncRoutes` using `router.addRoute` after login. |
| [`packages/web/utils/src/menuHelper.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/menuHelper.ts) | Builds the sidebar navigation from route `meta` fields. |

## Summary

- **Create** a TypeScript file under `apps/admin/src/router/routes/modules/` exporting a default `RouteRecordRaw` object.
- **Export** the route configuration as the default export so the glob loader can parse it correctly.
- **Control** sidebar visibility using `meta` properties like `shouldHideInMenu` and `orderNumber`.
- **Register** routes automatically through the file system, or manually via `router.addRoute()` for dynamic permissions.
- **Modify** [`packages/web/utils/src/menuHelper.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/menuHelper.ts) logic only if you need custom menu filtering beyond the standard `meta` fields.

## Frequently Asked Questions

### Where do I place new route definitions in Celeris Web?

Place all new route definitions as TypeScript files inside `apps/admin/src/router/routes/modules/`. The system recursively scans this directory using `import.meta.glob`, so files in subfolders are also discovered automatically.

### How does Celeris Web automatically discover new routes?

The router initialization in [`apps/admin/src/router/routes/index.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/router/routes/index.ts) uses Vite's `import.meta.glob("./modules/**/*.ts")` to eagerly import all module files at build time. The `loadRoutesFromModules` utility in [`packages/web/utils/src/router.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/router.ts) then converts these imports into the `asyncRoutes` array without requiring manual imports.

### Can I add routes dynamically after the app starts?

Yes. Use the `router.addRoute()` method from Vue Router as demonstrated in [`apps/admin/src/store/modules/user.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/store/modules/user.ts). This is useful for registering user-specific routes returned from an API after authentication, though most static features should use the file-based approach for better type safety and code splitting.

### How do I hide a route from the sidebar navigation?

Set `meta.shouldHideInMenu = true` in your route definition. The menu helper in [`packages/web/utils/src/menuHelper.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/utils/src/menuHelper.ts) filters out routes with this flag when building the sidebar navigation tree. You can also use `meta.shouldAffixToNavBar = true` to show the route in the top navigation instead.