Routing in modern web applications enables dynamic, client-side navigation without full page reloads. In Vue 3 with Vue Router 4, routing is declarative, composable, and highly extensible—supporting SPA navigation, parameterized paths, conditional access, and lifecycle-aware logic.
Core Routing Concepts
A route maps a URL pattern to a component or set of components. It serves as the backbone for navigation, state management across views, and user experience continuity—especially critical in single-page applications where seamless transitions replace traditional server round-trips.
Redirect Configuration
Redirection reroutes navigation from one path to another before rendering. It’s commonly used to normalize URLs, handle legacy routes, or enforce canonical entry points.
In router.js:
import { createRouter, createWebHashHistory } from 'vue-router';
import Dashboard from '../views/Dashboard.vue';
import ItemList from '../views/ItemList.vue';
import NewItem from '../views/NewItem.vue';
import EditItem from '../views/EditItem.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
components: { default: Dashboard }
},
{
path: '/items',
components: { listSlot: ItemList }
},
{
path: '/all-items',
redirect: '/items' // Redirects /all-items → /items
},
{
path: '/create',
components: { formSlot: NewItem }
},
{
path: '/edit/:id',
components: { editSlot: EditItem }
}
]
});
export default router;
In App.vue, multiple <router-view> slots render named components:
<template>
<nav>
<router-link to="/">Dashboard</router-link>
<router-link to="/items">Items</router-link>
<router-link to="/all-items">All Items (redirects)</router-link>
<router-link to="/create">New</router-link>
</nav>
<main>
<!-- Default view -->
<router-view />
<!-- Named views -->
<section><h3>List Panel</h3><router-view name="listSlot" /></section>
<section><h3>Form Panel</h3><router-view name="formSlot" /></section>
<section><h3>Editor Panel</h3><router-view name="editSlot" /></section>
</main>
</template>
Programmatic Navigation
Unlike static <router-link> elements, programmatic navigation uses the useRouter() composable to trigger navigation dynamically—ideal for form submissions, conditional redirects, or asynchronous workflows.
In App.vue:
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const targetPath = ref('/items');
const navigateToItems = () => {
// Navigate by string path
router.push('/items');
// Or navigate using a location descriptor
// router.push({ path: '/items', query: { filter: 'active' } });
};
const navigateWithParams = (id) => {
router.push({ name: 'editItem', params: { id } });
};
</script>
<template>
<button @click="navigateToItems">Go to Items</button>
<button @click="navigateWithParams(42)">Edit Item #42</button>
</template>
Passing Data via Routes
Two primary strategies exist for passing data between routes: path parameters and query parameters.
1. Path Parameters
Define dynamic segments in route paths using colons (:id). These values are accesible via route.params.
Route definition:
{
path: '/details/:lang/:version',
name: 'languageDetail',
component: LanguageDetail
}
In LanguageDetail.vue:
<script setup>
import { useRoute } from 'vue-router';
import { ref, onMounted } from 'vue';
const route = useRoute();
const language = ref(route.params.lang || 'unknown');
const version = ref(route.params.version || 'latest');
onMounted(() => {
console.log(`Displaying ${language.value} v${version.value}`);
});
</script>
<template>
<h2>{{ language }} (v{{ version }})</h2>
</template>
2. Query Praameters
Key-value pairs appended after ? in the URL (e.g., /search?term=vue&page=2). Accessed via route.query.
Navigation example:
router.push({
path: '/search',
query: { term: 'Vue Router', limit: '10' }
});
Inside the target component:
const route = useRoute();
console.log(route.query.term); // 'Vue Router'
Route Navigation Guards
Guards intercept navigation to enable authentication checks, data preloading, or confirmation prompts.
Global Before-Each Guard
Executes before every route change. Use it for authentication enforcement:
// router.js
router.beforeEach((to, from, next) => {
const isAuthenticated = !!localStorage.getItem('authToken');
if (to.meta.requiresAuth && !isAuthenticated) {
next({ path: '/login', query: { redirect: to.fullPath } });
} else if (to.path === '/login' && isAuthenticated) {
next('/dashboard');
} else {
next();
}
});
Add metadata to protected routes:
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
}
Global After-Each Hook
Fires after successful navigation—ideal for analytics, logging, or UI cleanup:
router.afterEach((to, from) => {
document.title = to.meta.title || 'App';
gtag('config', 'GA_MEASUREMENT_ID', { page_path: to.fullPath });
});
Authentication Flow Example
A login flow demonstrates guard integration:
Login.vuecollects credentials and stores a session token on success.Dashboard.vuereads the token and offers logout that clears storage and redirects.- The global guard blocks unauthenticated access to protected routes.
This pattern decouples authorization logic from individual components and centralizes security policy—ensuring consistent behavior across the application surface.