iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Vue Router

Vue Router is the official routing library: typed routes, nested layouts, lazy-loaded chunks, navigation guards, and dynamic imports. Compose with the Composition API (useRouter / useRoute) for clean access from any component. Adds zero CSS and ships under 10KB.

Routes, guards, lazy loading, nested layouts

EXAMPLE
// npm i vue-router
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

// 1) Route definitions — lazy-loaded chunks via () => import(...)
const routes: RouteRecordRaw[] = [
  { path: '/',        component: () => import('./pages/Home.vue') },
  { path: '/login',   component: () => import('./pages/Login.vue'), meta: { public: true } },

  {
    path: '/orders',
    component: () => import('./pages/OrdersLayout.vue'),
    meta: { requiresAuth: true },
    children: [
      { path: '',         component: () => import('./pages/OrdersList.vue') },
      { path: ':id',      name: 'order-detail',
                          component: () => import('./pages/OrderDetail.vue'),
                          props: true },
      { path: ':id/edit', component: () => import('./pages/OrderEdit.vue') },
    ],
  },

  { path: '/admin',   component: () => import('./pages/Admin.vue'),
                       meta: { requiresAuth: true, role: 'admin' } },

  { path: '/:pathMatch(.*)*', component: () => import('./pages/NotFound.vue') },
];

// 2) Build the router
export const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(_to, _from, savedPosition) {
    return savedPosition ?? { top: 0 };
  },
});

// 3) Global navigation guard — auth + role gating
import { useAuthStore } from './stores/auth';

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

  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    return { path: '/login', query: { next: to.fullPath } };
  }
  if (to.meta.role && auth.role !== to.meta.role) {
    return { path: '/403' };
  }
});

// 4) Per-route guard — for fine-grained checks
router.beforeResolve(async (to) => {
  if (to.matched.some((r) => r.meta.fetchUser)) {
    const auth = useAuthStore();
    await auth.fetchProfile();
  }
});

// 5) Mount it
// main.ts
// const app = createApp(App);
// app.use(router);
// app.mount('#app');

// 6) Use from a component (Composition API)
<script setup lang='ts'>
import { useRouter, useRoute } from 'vue-router';
import { computed, watch } from 'vue';

const router = useRouter();
const route  = useRoute();

const orderId = computed(() => route.params.id as string);

// React to route changes
watch(orderId, (id) => { console.log('viewing', id); });

function goHome() { router.push('/'); }
function goEdit() { router.push({ name: 'order-detail', params: { id: orderId.value } }); }
</script>

<!-- 7) <router-view> renders the nested route -->
<template>
  <header>
    <RouterLink to='/'>Home</RouterLink>
    <RouterLink to='/orders'>Orders</RouterLink>
  </header>

  <main>
    <!-- Nested routes render here -->
    <RouterView v-slot='{ Component, route }'>
      <Transition name='fade' mode='out-in'>
        <Component :is='Component' :key='route.fullPath' />
      </Transition>
    </RouterView>
  </main>
</template>

<!-- 8) <RouterLink> auto-detects active state -->
<RouterLink to='/orders' active-class='text-blue-600' exact-active-class='font-bold'>
  Orders
</RouterLink>

<!-- 9) Programmatic navigation patterns -->
<!-- router.push({ name: 'order-detail', params: { id: 'o1' } }) -->
<!-- router.replace('/login')  -- replaces history entry instead of pushing -->
<!-- router.back() / router.forward() / router.go(-2) -->

<!-- 10) Typed routes (Vue Router 4 + 'vue-router' types are fine; for fully typed
        routes via the unplugin, install 'unplugin-vue-router' which generates
        a typed RouteRecord union from your /pages folder.) -->

Why it matters

Use `meta` to encode permissions on routes (`requiresAuth`, `role`) and one global guard to read them. That single seam holds the entire access-control story for the SPA — adding a new gated route is just `meta: { requiresAuth: true }`, no copy-paste in every page.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
    history: createWebHistory(),
    routes: [{ path: '/', component: Home }],
});
Try it Yourself »

Discussion

Loading…