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