Skip to content

Dynamic Route Matching with Params ​

Learn how to create flexible routes that can match multiple URL patterns using dynamic segments.

Introduction ​

Very often we need to map routes with the given pattern to the same component. For example, we may have a User component which should be rendered for all users but with different user IDs. In Vue Router we can use a dynamic segment in the path to achieve that, we call that a param.

Basic Dynamic Routes ​

Dynamic segments are denoted by a colon : followed by the parameter name:

javascript
import User from './User.vue'

const routes = [
  // Dynamic segments start with a colon
  { path: '/users/:id', component: User },
]

Now URLs like /users/johnny and /users/jolyne will both map to the same route.

When a route is matched, the value of its params will be exposed as $route.params in every component:

vue
<template>
  <div>
    <!-- The current route is accessible as $route in the template -->
    User {{ $route.params.id }}
  </div>
</template>

Multiple Parameters ​

You can have multiple params in the same route, and they will map to corresponding fields on $route.params:

PatternMatched Path$route.params
/users/:username/users/eduardo{ username: 'eduardo' }
/users/:username/posts/:postId/users/eduardo/posts/123{ username: 'eduardo', postId: '123' }

Accessing Route Information ​

In addition to $route.params, the route object also exposes other useful information:

  • $route.query - URL query parameters
  • $route.hash - URL hash
  • $route.fullPath - Complete URL including query and hash
  • $route.matched - Array of matched route records

Composition API ​

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

const route = useRoute()

// Access parameters
console.log(route.params.id)
console.log(route.query)
console.log(route.hash)
</script>

Options API ​

vue
<script>
export default {
  created() {
    console.log(this.$route.params.id)
    console.log(this.$route.query)
    console.log(this.$route.hash)
  }
}
</script>

Reacting to Parameter Changes ​

When the user navigates from /users/johnny to /users/jolyne, the same component instance will be reused. Since both routes render the same component, this is more efficient than destroying the old instance and creating a new one. However, this also means that component lifecycle hooks will not be called.

Using Watchers ​

To react to params changes in the same component, you can watch the route object:

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

const route = useRoute()

watch(
  () => route.params.id,
  async (newId, oldId) => {
    // React to route changes
    console.log(`User changed from ${oldId} to ${newId}`)
    // Fetch new user data
    await fetchUserData(newId)
  }
)
</script>

Using Navigation Guards ​

Alternatively, use the beforeRouteUpdate navigation guard:

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

onBeforeRouteUpdate(async (to, from) => {
  // React to route changes
  if (to.params.id !== from.params.id) {
    userData.value = await fetchUser(to.params.id)
  }
})
</script>

Catch-all / 404 Routes ​

Regular params will only match characters between URL fragments, separated by /. If we want to match anything, we can use a custom param regexp:

javascript
const routes = [
  // Will match everything and put it under `$route.params.pathMatch`
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  
  // Will match anything starting with `/user-`
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

404 Not Found Example ​

vue
<!-- NotFound.vue -->
<template>
  <div>
    <h1>404 - Page Not Found</h1>
    <p>The page "{{ $route.params.pathMatch }}" could not be found.</p>
    <router-link to="/">Go Home</router-link>
  </div>
</template>

Programmatic Navigation to Catch-all Routes ​

When navigating to a catch-all route programmatically, you need to provide the path as an array:

javascript
router.push({
  name: 'NotFound',
  // Preserve current path and remove the first char to avoid starting with `//`
  params: { pathMatch: route.path.substring(1).split('/') },
  // Preserve existing query and hash
  query: route.query,
  hash: route.hash,
})

Advanced Patterns ​

Vue Router supports many advanced matching patterns:

  • Optional parameters: /users/:id?
  • Zero or more: /files/*
  • One or more: /files/+
  • Custom regex: /users/:id(\\d+)

For more details, see the Advanced Matching Patterns documentation.

Best Practices ​

1. Validate Parameters ​

Always validate route parameters before using them:

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

const route = useRoute()

const userId = computed(() => {
  const id = route.params.id
  return /^\d+$/.test(id) ? parseInt(id) : null
})

const isValidUser = computed(() => userId.value !== null)
</script>

<template>
  <div v-if="isValidUser">
    User ID: {{ userId }}
  </div>
  <div v-else>
    Invalid user ID
  </div>
</template>

2. Handle Loading States ​

Show loading indicators when fetching data based on route parameters:

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

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

const fetchUser = async (id) => {
  loading.value = true
  try {
    user.value = await api.getUser(id)
  } catch (error) {
    console.error('Failed to fetch user:', error)
  } finally {
    loading.value = false
  }
}

// Watch for parameter changes
watch(
  () => route.params.id,
  (newId) => {
    if (newId) {
      fetchUser(newId)
    }
  },
  { immediate: true }
)
</script>

3. Use TypeScript for Better Type Safety ​

typescript
interface RouteParams {
  id: string
}

const route = useRoute<RouteParams>()
// route.params.id is now typed as string

Common Pitfalls ​

  1. Forgetting to watch parameter changes - Component instances are reused
  2. Not validating parameters - Always check if parameters are in expected format
  3. Incorrect catch-all patterns - Use (.*)* for proper catch-all behavior
  4. Missing error handling - Always handle cases where data fetching fails

Next Steps ​

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择