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…