Route params and navigation
Every blog post has its own page: /posts/1, /posts/2 and so on. A route per post is impossible, so the address declares a param — a changing part that starts with a colon. The component reads it and loads the right post.
A route with a param
Add a route to src/router/index.js:
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import PostView from '../views/PostView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{ path: '/', name: 'home', component: HomeView },
{ path: '/about', name: 'about', component: AboutView },
{ path: '/posts/:id', name: 'post', component: PostView },
],
})
export default routerThe address /posts/7 matches this route, and the id param gets the value '7' — always a string.
Reading the param
Create src/views/PostView.vue. The useRoute function returns the current route:
<!-- src/views/PostView.vue -->
<script setup>
import { useRoute } from 'vue-router'
import { useFetch } from '../composables/useFetch.js'
const route = useRoute()
const { data: post, loading, error } = useFetch(
`https://jsonplaceholder.typicode.com/posts/${route.params.id}`,
)
</script>
<template>
<p v-if="loading">Loading...</p>
<p v-else-if="error">Post not found.</p>
<article v-else>
<h1>{{ post.title }}</h1>
<p>{{ post.body }}</p>
</article>
</template>Open http://localhost:5173/posts/1 — the title and text of the first post appear. The useFetch composable comes from the data fetching lesson.
Links to posts
In src/components/PostList.vue turn the titles into links:
<!-- src/components/PostList.vue, template fragment -->
<li v-for="post in posts" :key="post.id">
<RouterLink :to="{ name: 'post', params: { id: post.id } }">
{{ post.title }}
</RouterLink>
</li>Clicking a title on the home page opens the post page. A string works too: :to="'/posts/' + post.id", but a link by name does not depend on the address format.
Navigating from code
Sometimes you navigate not on a link click but after an action — for example, returning to the list. That is what useRouter is for:
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function goHome() {
router.push({ name: 'home' })
}
</script>
<template>
<button @click="goHome">Back to posts</button>
</template>The button opens the home page.
| Call | What it does |
|---|---|
router.push(...) | Navigates with a new history entry |
router.replace(...) | Navigates replacing the current entry |
router.back() | Goes one step back, like the browser button |
Query params
The part of the address after ? is available in route.query: for /posts?page=2, route.query.page is '2'.
Common mistake
Going from /posts/1 to /posts/2 with a "Next post" link and seeing the old text. The router does not recreate the component when only the param changes — PostView stays the same, and its loading does not run again. Watch the param with watch(() => route.params.id, ...) and load the post again, or add a key in App.vue: <RouterView :key="$route.fullPath" /> — then the component is recreated for every new address.