Vue.js Route Parameters
Introduction
When building modern web applications, we often need to create dynamic routes that can adapt to different content. For example, you might want to display product details at /products/1
, /products/2
, etc., without creating a separate route for each product. This is where route parameters come into play in Vue Router.
Route parameters allow you to define dynamic segments in your route paths that capture values from the URL and make them available to your components. They are a fundamental concept in building flexible navigation systems for Vue.js applications.
Basic Route Parameters
Defining Routes with Parameters
In Vue Router, you can define route parameters by prefixing a segment of your route path with a colon (:
). This tells Vue Router that this segment is a parameter that can have any value.
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import ProductList from '../views/ProductList.vue'
import ProductDetail from '../views/ProductDetail.vue'
const routes = [
{
path: '/products',
name: 'ProductList',
component: ProductList
},
{
path: '/products/:id',
name: 'ProductDetail',
component: ProductDetail
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
In the example above, :id
is a route parameter. It will match any value in that position of the URL, such as /products/1
, /products/abc
, etc.
Accessing Route Parameters
You can access route parameters in your component in several ways:
Using $route.params
<!-- views/ProductDetail.vue -->
<template>
<div>
<h1>Product Details</h1>
<p>Product ID: {{ $route.params.id }}</p>
</div>
</template>
Using the Composition API
<template>
<div>
<h1>Product Details</h1>
<p>Product ID: {{ productId }}</p>
</div>
</template>
<script>
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute()
const productId = computed(() => route.params.id)
return {
productId
}
}
}
</script>
Using Props (Recommended Approach)
You can configure your route to pass parameters as props to your component, which creates a cleaner separation between your component and the routing system:
// router/index.js
const routes = [
// ...
{
path: '/products/:id',
name: 'ProductDetail',
component: ProductDetail,
props: true // Pass route.params as component props
}
]
Then in your component:
<template>
<div>
<h1>Product Details</h1>
<p>Product ID: {{ id }}</p>
</div>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true
}
}
}
</script>
Advanced Parameter Usage
Optional Parameters
You can make a route parameter optional by adding a question mark (?
) after it:
const routes = [
{
path: '/user/:username?',
component: UserProfile
}
]
This route will match both /user/john
and /user/
.
Multiple Parameters
You can define multiple parameters in a single route:
const routes = [
{
path: '/posts/:category/:id',
component: Post
}
]
This route will match /posts/tech/123
and make both category
and id
available as parameters.
Parameter Processing with Props Function
Sometimes you need to process parameters before they reach your component. You can do this by using a function for the props
option:
const routes = [
{
path: '/products/:id',
component: ProductDetail,
props: route => ({
id: Number(route.params.id),
showReviews: route.query.reviews === 'true'
})
}
]
This will convert the id
parameter to a number and also pass additional data based on query parameters.
Practical Examples
Building a Blog with Dynamic Routes
Let's build a simple blog that uses route parameters:
// router/index.js
const routes = [
{
path: '/blog',
name: 'BlogHome',
component: BlogHome
},
{
path: '/blog/:slug',
name: 'BlogPost',
component: BlogPost,
props: true
}
]
<!-- views/BlogHome.vue -->
<template>
<div>
<h1>Blog Posts</h1>
<ul>
<li v-for="post in posts" :key="post.id">
<router-link :to="{ name: 'BlogPost', params: { slug: post.slug } }">
{{ post.title }}
</router-link>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
posts: [
{ id: 1, title: 'Getting Started with Vue', slug: 'getting-started-with-vue' },
{ id: 2, title: 'Vue Router Basics', slug: 'vue-router-basics' },
{ id: 3, title: 'Working with Route Parameters', slug: 'working-with-route-parameters' }
]
}
}
}
</script>
<!-- views/BlogPost.vue -->
<template>
<div>
<h1>{{ currentPost.title }}</h1>
<p>{{ currentPost.content }}</p>
<router-link :to="{ name: 'BlogHome' }">Back to Blog</router-link>
</div>
</template>
<script>
export default {
props: {
slug: {
type: String,
required: true
}
},
data() {
return {
blogPosts: [
{
slug: 'getting-started-with-vue',
title: 'Getting Started with Vue',
content: 'Vue.js is a progressive framework for building user interfaces...'
},
{
slug: 'vue-router-basics',
title: 'Vue Router Basics',
content: 'Vue Router is the official router for Vue.js...'
},
{
slug: 'working-with-route-parameters',
title: 'Working with Route Parameters',
content: 'Route parameters allow you to capture parts of the URL...'
}
]
}
},
computed: {
currentPost() {
return this.blogPosts.find(post => post.slug === this.slug) || {
title: 'Post Not Found',
content: 'The requested post does not exist.'
}
}
}
}
</script>
E-commerce Product Page
Here's a real-world example of a product detail page that uses route parameters:
<!-- views/ProductDetail.vue -->
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">{{ error }}</div>
<div v-else class="product-detail">
<h1>{{ product.name }}</h1>
<div class="price">${{ product.price }}</div>
<div class="description">{{ product.description }}</div>
<button @click="addToCart">Add to Cart</button>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
props: {
id: {
type: String,
required: true
}
},
setup(props) {
const product = ref(null)
const loading = ref(true)
const error = ref(null)
const fetchProduct = async () => {
loading.value = true
try {
// In a real app, this would be an API call
// const response = await fetch(`/api/products/${props.id}`)
// product.value = await response.json()
// Simulated API response
await new Promise(resolve => setTimeout(resolve, 500))
product.value = {
id: props.id,
name: `Product ${props.id}`,
price: Math.floor(Math.random() * 100) + 10,
description: `This is a detailed description for product ${props.id}.`
}
} catch (err) {
error.value = "Failed to load product details"
console.error(err)
} finally {
loading.value = false
}
}
const addToCart = () => {
alert(`Added ${product.value.name} to cart!`)
}
onMounted(() => {
fetchProduct()
})
return {
product,
loading,
error,
addToCart
}
}
}
</script>
Navigation with Parameters
Programmatic Navigation
You can navigate to routes with parameters programmatically:
// Navigate to a product detail page
router.push({ name: 'ProductDetail', params: { id: '123' } })
// Or with path (less recommended)
router.push(`/products/123`)
Using <router-link>
with Parameters
<template>
<div>
<h2>Product List</h2>
<ul>
<li v-for="product in products" :key="product.id">
<!-- Using named routes (recommended) -->
<router-link :to="{ name: 'ProductDetail', params: { id: product.id } }">
{{ product.name }}
</router-link>
<!-- Or using path -->
<!-- <router-link :to="`/products/${product.id}`">{{ product.name }}</router-link> -->
</li>
</ul>
</div>
</template>
Reacting to Parameter Changes
When navigating between routes with the same component but different parameters (like /products/1
to /products/2
), Vue Router reuses the same component instance. This means component lifecycle hooks like created
or mounted
won't be called again.
To react to parameter changes, you have several options:
1. Watch the route parameter
<script>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
export default {
props: ['id'],
setup(props) {
// Option 1: Watch the prop directly
watch(() => props.id, (newId) => {
console.log(`ID changed to ${newId}, fetching new data...`)
fetchData(newId)
})
// Option 2: Watch the route
const route = useRoute()
watch(
() => route.params.id,
(newId) => {
console.log(`ID changed to ${newId}, fetching new data...`)
fetchData(newId)
}
)
function fetchData(id) {
// Fetch data for the new ID
}
// Initial fetch
fetchData(props.id)
}
}
</script>
2. Use the Navigation Guard
<script>
export default {
props: ['id'],
data() {
return {
product: null
}
},
methods: {
fetchData(id) {
// Fetch product data
console.log(`Fetching product ${id}`)
this.product = { id, name: `Product ${id}` }
}
},
created() {
this.fetchData(this.id)
},
beforeRouteUpdate(to, from, next) {
// React to route changes when the current component is being reused
const newId = to.params.id
this.fetchData(newId)
next()
}
}
</script>
Summary
Route parameters in Vue Router are a powerful feature that allows you to create dynamic, data-driven applications. In this guide, we've explored:
- How to define routes with parameters using the colon syntax (
:paramName
) - Different ways to access route parameters in components
- Advanced parameter usage including optional parameters and multiple parameters
- Practical examples of route parameters in blog and e-commerce applications
- Techniques for navigating with parameters and handling parameter changes
Understanding route parameters is essential for building flexible and responsive Vue.js applications that can dynamically display content based on the URL.
Exercises
To reinforce your understanding of route parameters, try these exercises:
-
Create a simple user profile system with routes like
/users/:username
that displays different information based on the username parameter. -
Build a photo gallery app with nested routes:
/gallery/:albumId
and/gallery/:albumId/photo/:photoId
. -
Create a route with optional parameters that shows filtered results, like
/products/:category?
that shows all products when no category is provided. -
Implement a "related products" feature that allows users to navigate between related products while staying on the same component.
Additional Resources
- Vue Router Official Documentation on Dynamic Route Matching
- Vue Router Navigation Guards
- Vue Router API Reference
Remember that route parameters are just one part of building a robust routing system. Combined with other Vue Router features like nested routes, navigation guards, and route meta fields, you can create sophisticated navigation patterns for your Vue.js applications.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)