aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

Vue Router: Routes, Params and Navigation Guards

Vue Router is the official solution for single-page (SPA) routing in Vue 3 apps: it changes the URL without a full reload, renders the right component, and works correctly with the browser's back and forward buttons. This guide covers route definitions, dynamic params, programmatic navigation, and the navigation guards that drive access control, with real examples. Everything here targets Vue Router 4 (the version used with Vue 3).

Installation and a basic route definition

Install the package and create a router instance. createRouter takes a history mode and a routes array:

npm install vue-router@4
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

const routes = [
  { path: '/', name: 'home', component: Home },
  { path: '/about', name: 'about', component: About },
]

export const router = createRouter({
  history: createWebHistory(),
  routes,
})

Then wire the router into the app:

// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import { router } from './router'

createApp(App).use(router).mount('#app')

<router-view> decides where the matched component is rendered, and you create links with <router-link> so there is no full page reload:

<router-link to="/">Home</router-link>
<router-link :to="{ name: 'about' }">About</router-link>
<router-view />

Giving routes a name is a good habit: even if the URL structure changes, links built with name keep working.

history mode: web history or hash?

There are two common choices. createWebHistory() produces clean URLs (/about) but requires your server to redirect every unknown path to index.html; otherwise you get a 404 on reload. createWebHashHistory() adds a # to the URL (/#/about) and needs no server config — handy for static hosting, but weaker aesthetically and for SEO. Most modern projects prefer web history and add a fallback rule on the server.

Dynamic params

Segments in a path that start with : are dynamic params. For example a detail page:

const routes = [
  { path: '/users/:id', name: 'user', component: UserDetail },
]

Inside the component you read the param with useRoute(). A typical Composition API usage looks like this:

<script setup>
import { useRoute } from 'vue-router'
import { watch, ref } from 'vue'

const route = useRoute()
const user = ref(null)

async function load(id) {
  user.value = await fetch(`/api/users/${id}`).then(r => r.json())
}

load(route.params.id)

// Watch param changes when the same component is reused
watch(() => route.params.id, (id) => load(id))
</script>

There is a critical point here: when the user goes from /users/1 to /users/2, Vue reuses the same component instance, so the component is not recreated. That is why you must watch route.params.id to refetch the data. If you prefer to receive the param as a prop, set props: true on the route; your component then receives id as a plain prop and becomes easier to test.

Use :id? for an optional param, and the /:pathMatch(.*)* pattern to catch multiple segments; the latter is typically used for a 404 page.

Programmatic navigation and query params

To navigate from code after a form is submitted or an action completes, use useRouter():

<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

function goToUser(id) {
  router.push({ name: 'user', params: { id } })
}

function search(term) {
  router.push({ path: '/search', query: { q: term } })
}
</script>

Query params (?q=...) are read through route.query. Use router.replace() to change the current step without leaving a new history entry, and router.back() to go back.

Navigation guards: access control

Guards let you step in before a transition happens and allow, redirect, or cancel it — the core tool for authentication. The most common global guard is beforeEach:

router.beforeEach((to, from) => {
  const isAuth = !!localStorage.getItem('token')
  if (to.meta.requiresAuth && !isAuth) {
    // Returning a location object redirects the transition there
    return { name: 'login', query: { redirect: to.fullPath } }
  }
  // Returning true or nothing allows the transition
})

The modern style in Vue Router 4 is to return a value instead of calling next(): false cancels the transition, a location object redirects, and anything else (or undefined) allows it. You mark which routes are protected with the meta field on the route definition:

{
  path: '/dashboard',
  name: 'dashboard',
  component: Dashboard,
  meta: { requiresAuth: true },
}

If you only need to protect a single route, use that route's own beforeEnter guard. Inside a component, onBeforeRouteLeave lets you run checks like "you have unsaved changes, are you sure you want to leave?"

Lazy loading and nested routes

In large apps, loading every view up front slows the initial open. By passing the component as a dynamic import() you get route-level code splitting; that chunk is downloaded only when the route is visited:

const routes = [
  { path: '/about', component: () => import('../views/About.vue') },
]

For nested layouts (for example tabs on a user profile) use children and place a second <router-view> in the parent component's template:

{
  path: '/users/:id',
  component: UserLayout,
  children: [
    { path: '', component: UserOverview },
    { path: 'posts', component: UserPosts },
  ],
}

Frequently Asked Questions

Why doesn't my page update when the param changes?

Because Vue Router reuses the same component instead of recreating it. Either watch route.params and refetch the data, or give the component a unique :key to force a remount.

Why do I get a 404 when I refresh the page?

In web history mode your server does not know about paths like /about. Configure the server to redirect all unmatched requests to index.html (the SPA fallback); if you cannot, use hash history instead.

Should I use next() or return inside beforeEach?

In Vue Router 4, returning a value is recommended; it is cleaner and less error-prone. next() still works, but do not mix the two in the same guard, or the transition may be processed twice.

Has routing in your Vue project gotten messy? Let's review your route structure, guards, and code splitting together and turn it into a solid architecture. Get in touch and we'll build a solution that fits your needs.

Bu kategorideki tüm yazılar →

Devamı için