Dynamic Routes and Route Params
Real applications rarely have a fixed, finite set of pages — think of a product detail page, a user profile, or a blog post, where the same component template needs to render different data depending on which item was requested. Vue Router supports this through dynamic route segments, denoted with a colon prefix in the path definition, such as /users/:id. When a URL matches this pattern, the matched value is exposed as a route param that the component can read to fetch or display the correct data.
Cricket analogy: A dynamic route like /players/:id is like a single scorecard template that renders any player's stats depending on which id the broadcaster requests, rather than needing a separate hardcoded page for Kohli, Root, and every other player.
Defining Dynamic Segments
A single route definition can contain multiple dynamic segments, and segments can be combined with static path parts. Route params are always extracted as strings, even if the URL segment looks numeric, so components must convert types explicitly when needed (e.g. Number(route.params.id)).
Cricket analogy: Combining a dynamic segment with a static part, like /teams/:teamId/players/:playerId, is like a scorecard URL naming both the team and the player, but the id always arrives as text, so a batting average must be explicitly converted to a number before math is done on it.
const routes = [
{ path: '/users/:id', name: 'user-detail', component: UserDetail },
{ path: '/posts/:category/:slug', name: 'post-detail', component: PostDetail },
]Reading Params Reactively
Inside a component, useRoute() returns a reactive route object; route.params.id updates automatically as the URL changes. Critically, because Vue Router can reuse the same component instance when navigating between two routes that match the same component (e.g. /users/1 to /users/2), a plain onMounted fetch is not enough — you must watch the params to refetch data when they change.
Cricket analogy: useRoute() is like a live scoreboard feed that updates route.params.id as the broadcast switches from one player's stats to another; since the same commentary box is reused, you must watch for the id change to pull fresh stats rather than fetching once and going stale.
<script setup>
import { ref, watchEffect } from 'vue'
import { useRoute } from 'vue-router'
import { fetchUser } from '@/api/users'
const route = useRoute()
const user = ref(null)
watchEffect(async () => {
user.value = await fetchUser(route.params.id)
})
</script>
<template>
<h2 v-if="user">{{ user.name }}</h2>
</template>You can also access route params as props by setting props: true on the route definition. This decouples the component from vue-router directly (useful for testing) since it receives id as a normal component prop instead of reading useRoute() internally.
A very common bug: using onMounted(() => fetchUser(route.params.id)) assumes the component remounts on every param change. Because Vue Router reuses component instances for routes sharing the same component, onMounted only fires once — navigating from /users/1 to /users/2 silently shows stale data unless you watch route.params or route itself.
- Dynamic segments are declared with a colon, e.g. /users/:id, and matched dynamically at runtime.
- Route params are always strings; convert types explicitly (Number(), etc.) when needed.
- useRoute().params gives reactive access to the current route's dynamic segment values.
- Vue Router reuses component instances across param-only navigation, so onMounted alone will miss updates.
- watch or watchEffect on route.params is the correct pattern for refetching data on param change.
- Setting props: true on a route lets the matched component receive params as plain props instead of via useRoute().
Practice what you learned
1. How is a dynamic segment declared in a Vue Router path?
2. What data type are values in route.params always returned as?
3. Why can relying solely on onMounted to fetch data cause bugs when navigating between /users/1 and /users/2?
4. What does setting props: true on a route definition accomplish?
5. Which composable provides reactive access to the current route's params inside <script setup>?
Was this page helpful?
You May Also Like
Vue Router Basics
Learn how Vue Router maps URLs to components, enabling single-page applications to feel like multi-page sites without full browser reloads.
Nested Routes
Learn how to compose layouts with parent and child routes so nested UI sections render inside their own router-view outlets.
Fetching Data in Vue
Learn common patterns for fetching data in Vue components and composables, including lifecycle timing, reactivity pitfalls, and race conditions.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics