Skip to content

Router ​

Routes are defined in src/resources/src/router/index.ts using Vue Router with createWebHistory.

ts
import { createRouter, createWebHistory } from "vue-router";
import { setupRouteProgress } from "@/plugins/routeProgress";
import { useAuthStore } from "@/stores/auth";

export const routes = [
  {
    path: "/",
    name: "dashlayout",
    component: () => import("@/layouts/Layout/index.vue"),
    redirect: { path: "/" },
    children: [
      {
        path: "/",
        name: "dashboard",
        component: () => import("@/pages/dashboard/index.vue"),
        meta: { requiresAuth: true },
      },
      // Add your authenticated pages here
    ],
  },
  {
    path: "/login",
    name: "authlayout",
    component: () => import("@/layouts/AuthLayout.vue"),
    children: [
      {
        path: "/login",
        name: "login",
        component: () => import("@/pages/auth/login.vue"),
        meta: { guestOnly: true },
      },
      {
        path: "/register",
        name: "register",
        component: () => import("@/pages/auth/register.vue"),
        meta: { guestOnly: true },
      },
      {
        path: "/forget-password",
        name: "forget-password",
        component: () => import("@/pages/auth/forgetPassword.vue"),
        meta: { guestOnly: true },
      },
      {
        path: "/reset-password",
        name: "reset-password",
        component: () => import("@/pages/auth/resetPassword.vue"),
        meta: { guestOnly: true },
      },
      {
        path: "/verify-email",
        name: "verify-email",
        component: () => import("@/pages/auth/verifyEmail.vue"),
        meta: { guestOnly: true },
      },
    ],
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior: (_to, _from, savedPosition) => {
    if (savedPosition) return savedPosition;
    return { top: 0 };
  },
});

setupRouteProgress(router);

router.beforeEach(async (to) => {
  const auth = useAuthStore();
  await auth.bootstrap();

  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    return { path: "/login", query: { redirect: to.fullPath } };
  }
  if (to.meta.guestOnly && auth.isAuthenticated) {
    return { path: "/" };
  }
  return true;
});

export default router;

Route structure ​

Two parent layouts with nested child routes:

  • dashlayout (/) — Dashboard layout with Sidebar + Header + Footer, wraps all authenticated pages
  • authlayout (/login) — Centered card layout for guest-only auth pages

Route meta flags ​

FlagPurpose
requiresAuth: trueRedirects to /login if unauthenticated
guestOnly: trueRedirects to / if already authenticated

Auth guard ​

Every navigation runs router.beforeEach which:

  1. Calls auth.bootstrap() to restore or verify the session
  2. Checks requiresAuth — redirects to /login?redirect=<path> if not authenticated
  3. Checks guestOnly — redirects to / if already authenticated

Adding a new page ​

ts
// Add a route child under "dashlayout"
children: [
  {
    path: "/posts",
    name: "posts",
    component: () => import("@/pages/posts/index.vue"),
    meta: { requiresAuth: true },
  },
];

// Create the page component at src/resources/src/pages/posts/index.vue

Lazy loading ​

All page components use dynamic import() so they are code-split per route by Vite.

Route progress bar ​

setupRouteProgress(router) adds a thin gradient progress bar at the top of the page that animates on navigation start and completes on navigation end.

Released under the MIT License.