Skip to content

Programmatic Navigation ​

Learn how to navigate between routes programmatically using Vue Router's navigation methods.

Introduction ​

Besides using <router-link> for declarative navigation, you can navigate programmatically using the router's instance methods. This is useful for navigation based on user interactions, form submissions, or business logic.

Accessing the Router ​

Options API ​

javascript
export default {
  methods: {
    navigateToUser() {
      this.$router.push('/user/123')
    }
  }
}

Composition API ​

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

const router = useRouter()

const navigateToUser = () => {
  router.push('/user/123')
}
</script>

router.push() ​

Navigate to a different location by pushing a new entry into the history stack:

javascript
// String path
router.push('/users/eduardo')

// Object with path
router.push({ path: '/users/eduardo' })

// Named route with params
router.push({ name: 'user', params: { username: 'eduardo' } })

// With query parameters
router.push({ path: '/register', query: { plan: 'private' } })
// Results in: /register?plan=private

// With hash
router.push({ path: '/about', hash: '#team' })
// Results in: /about#team

Equivalent to clicking <router-link :to="...">

DeclarativeProgrammatic
<router-link :to="...">router.push(...)

router.replace() ​

Navigate without pushing a new history entry (replaces the current entry):

javascript
// Replace current location
router.replace('/home')

// Object syntax
router.replace({ path: '/home' })

// Add replace: true to push
router.push({ path: '/home', replace: true })
DeclarativeProgrammatic
<router-link :to="..." replace>router.replace(...)

router.go() ​

Traverse the history stack:

javascript
// Go forward one record
router.go(1)
// Same as router.forward()

// Go back one record  
router.go(-1)
// Same as router.back()

// Go forward 3 records
router.go(3)

// Go back 3 records
router.go(-3)

// Fails silently if not enough records
router.go(-100)
router.go(100)

Using Parameters ​

When using named routes, always prefer name + params over manual path construction:

javascript
const username = 'eduardo'

// ✅ Recommended: Automatic URL encoding
router.push({ name: 'user', params: { username } })

// ✅ Manual path construction
router.push(`/user/${username}`)
router.push({ path: `/user/${username}` })

// ❌ Wrong: params ignored when path is provided
router.push({ path: '/user', params: { username } })

Parameter Types ​

Parameters should be strings, numbers, or arrays of these:

javascript
// ✅ Valid parameter types
router.push({ name: 'user', params: { id: '123' } })
router.push({ name: 'user', params: { id: 123 } })
router.push({ name: 'tags', params: { tags: ['vue', 'router'] } })

// ⚠️ Other types are automatically stringified
router.push({ name: 'user', params: { id: { value: 123 } } })
// Results in: id: "[object Object]"

// ✅ For optional params, use empty string or null
router.push({ name: 'user', params: { id: '' } })  // Removes param
router.push({ name: 'user', params: { id: null } }) // Removes param

Query Parameters ​

Query parameters can be combined with any navigation method:

javascript
// Simple query
router.push({ path: '/search', query: { q: 'vue' } })
// Results in: /search?q=vue

// Multiple query parameters
router.push({
  path: '/products',
  query: { 
    category: 'electronics',
    sort: 'price',
    page: 1
  }
})
// Results in: /products?category=electronics&sort=price&page=1

// Array values
router.push({
  path: '/filter',
  query: { tags: ['vue', 'javascript'] }
})
// Results in: /filter?tags=vue&tags=javascript

Async Navigation ​

All navigation methods return a Promise:

javascript
// Wait for navigation to complete
try {
  await router.push('/user/123')
  console.log('Navigation successful')
} catch (error) {
  console.log('Navigation failed:', error)
}

// Handle navigation result
router.push('/user/123')
  .then(() => {
    console.log('Navigation successful')
  })
  .catch(error => {
    console.log('Navigation failed:', error)
  })

Navigation can fail for several reasons:

javascript
import { NavigationFailureType, isNavigationFailure } from 'vue-router'

try {
  await router.push('/user/123')
} catch (error) {
  if (isNavigationFailure(error, NavigationFailureType.aborted)) {
    console.log('Navigation was aborted')
  } else if (isNavigationFailure(error, NavigationFailureType.cancelled)) {
    console.log('Navigation was cancelled')
  } else if (isNavigationFailure(error, NavigationFailureType.duplicated)) {
    console.log('Navigation was duplicated')
  }
}

Practical Examples ​

Form Submission Navigation ​

vue
<template>
  <form @submit.prevent="submitForm">
    <input v-model="username" placeholder="Username" required />
    <button type="submit" :disabled="loading">
      {{ loading ? 'Creating...' : 'Create User' }}
    </button>
  </form>
</template>

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

const router = useRouter()
const username = ref('')
const loading = ref(false)

const submitForm = async () => {
  loading.value = true
  
  try {
    // Create user via API
    const user = await createUser(username.value)
    
    // Navigate to user profile
    await router.push({
      name: 'user-profile',
      params: { id: user.id }
    })
  } catch (error) {
    console.error('Failed to create user:', error)
  } finally {
    loading.value = false
  }
}
</script>

Conditional Navigation ​

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

const router = useRouter()
const route = useRoute()

const handleNavigation = async (targetRoute) => {
  // Check if user has unsaved changes
  if (hasUnsavedChanges.value) {
    const confirmed = await showConfirmDialog(
      'You have unsaved changes. Are you sure you want to leave?'
    )
    
    if (!confirmed) {
      return
    }
  }
  
  // Navigate to target route
  router.push(targetRoute)
}

const goBack = () => {
  // Go back or to home if no history
  if (window.history.length > 1) {
    router.go(-1)
  } else {
    router.push('/')
  }
}
</script>

Dynamic Route Building ​

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

const router = useRouter()
const route = useRoute()

// Build routes dynamically based on current route
const navigationItems = computed(() => [
  {
    name: 'Dashboard',
    route: { name: 'dashboard' }
  },
  {
    name: 'Profile',
    route: { 
      name: 'user-profile', 
      params: { id: route.params.id } 
    }
  },
  {
    name: 'Settings',
    route: { 
      name: 'user-settings', 
      params: { id: route.params.id },
      query: { tab: 'general' }
    }
  }
])

const navigateTo = (item) => {
  router.push(item.route)
}
</script>
vue
<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

const openUserInNewContext = (userId) => {
  // Navigate with additional state
  router.push({
    name: 'user-detail',
    params: { id: userId },
    query: { 
      returnTo: route.fullPath,
      context: 'search-results'
    }
  })
}

const returnToPrevious = () => {
  const returnTo = route.query.returnTo
  if (returnTo) {
    router.push(returnTo)
  } else {
    router.go(-1)
  }
}
</script>

Best Practices ​

1. Use Named Routes for Complex Navigation ​

javascript
// ✅ Good: Clear and maintainable
router.push({
  name: 'product-detail',
  params: { id: productId },
  query: { variant: selectedVariant }
})

// ❌ Avoid: Hard to maintain
router.push(`/products/${productId}?variant=${selectedVariant}`)

2. Handle Navigation Errors ​

javascript
const navigateWithErrorHandling = async (route) => {
  try {
    await router.push(route)
  } catch (error) {
    if (!isNavigationFailure(error)) {
      // Unexpected error
      console.error('Navigation error:', error)
      showErrorMessage('Navigation failed. Please try again.')
    }
  }
}

3. Validate Before Navigation ​

javascript
const navigateToProtectedRoute = async (route) => {
  // Check authentication
  if (!isAuthenticated.value) {
    router.push({ name: 'login', query: { redirect: route.fullPath } })
    return
  }
  
  // Check permissions
  if (!hasPermission(route.meta.requiredPermission)) {
    router.push({ name: 'unauthorized' })
    return
  }
  
  router.push(route)
}

4. Preserve Query Parameters ​

javascript
const navigatePreservingQuery = (newPath) => {
  router.push({
    path: newPath,
    query: route.query // Preserve current query parameters
  })
}

Common Pitfalls ​

  1. Mixing path and params - Use either path or name + params, not both
  2. Forgetting await - Navigation is asynchronous, use await when needed
  3. Not handling errors - Always handle potential navigation failures
  4. Incorrect parameter types - Use strings/numbers for route parameters

Next Steps ​

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