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 - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择