Let's explore sophisticated navigation patterns used in real applications.
Router Typing with TypeScript
typescript// router/index.ts import type { RouteRecordRaw } from 'vue-router' const routes: RouteRecordRaw[] = [ { path: '/users/:id', name: 'user', component: () => import('@/views/User.vue'), meta: { requiresAuth: true, title: 'User Profile' } } ] // Type-safe route names type RouteNames = 'home' | 'user' | 'settings' // Custom typed push function function typedPush(name: RouteNames, params?: Record<string, string>) { return router.push({ name, params }) }
Preserving Query Parameters
typescriptfunction navigatePreservingQuery(path: string) { router.push({ path, query: route.query // Keep existing query params }) } // Add to existing query function addQueryParam(key: string, value: string) { router.push({ query: { ...route.query, [key]: value } }) } // Remove a query param function removeQueryParam(key: string) { const { [key]: removed, ...rest } = route.query router.push({ query: rest }) }
Navigation with State
Pass state without showing in URL:
typescript// Navigate with state router.push({ name: 'user', params: { id: 123 }, state: { fromNotification: true } // Hidden state }) // Access in destination const state = history.state if (state?.fromNotification) { showWelcomeMessage() }
Scroll Behavior
Control scroll position on navigation:
typescriptconst router = createRouter({ history: createWebHistory(), routes, scrollBehavior(to, from, savedPosition) { // Back/forward navigation: restore position if (savedPosition) { return savedPosition } // Navigate to hash anchor if (to.hash) { return { el: to.hash, behavior: 'smooth' } } // Same route navigation: don't scroll if (to.path === from.path) { return false } // Default: scroll to top return { top: 0, behavior: 'smooth' } } })
Delayed Scroll (Wait for Content)
typescriptscrollBehavior(to, from, savedPosition) { return new Promise((resolve) => { setTimeout(() => { resolve({ top: 0 }) }, 500) // Wait for transitions }) }
Route Aliases
Multiple URLs for the same component:
typescriptconst routes = [ { path: '/users', component: Users, alias: ['/people', '/members'] // All show Users component }, { path: '/home', component: Home, alias: '/' // Both / and /home show Home } ]
Redirects
typescriptconst routes = [ // Simple redirect { path: '/old-page', redirect: '/new-page' }, // Named route redirect { path: '/user/:id/profile', redirect: { name: 'user' } // Keeps params }, // Function redirect (dynamic) { path: '/search/:query', redirect: (to) => { return { path: '/results', query: { q: to.params.query } } } }, // Redirect all unknown routes { path: '/:pathMatch(.*)*', redirect: '/404' } ]
Navigation Failures
Handle navigation failures gracefully:
typescriptimport { NavigationFailureType, isNavigationFailure } from 'vue-router' async function navigate() { const failure = await router.push('/dashboard') if (failure) { if (isNavigationFailure(failure, NavigationFailureType.aborted)) { // Navigation was aborted (by a guard) console.log('Navigation aborted') } if (isNavigationFailure(failure, NavigationFailureType.duplicated)) { // Already on that route console.log('Already there') } } }
Router Ready State
Wait for initial navigation:
typescript// main.ts router.isReady().then(() => { app.mount('#app') }) // Or in a component await router.isReady() console.log('Router is ready')