Skip to content

RouteLocation

Route location type, describing the target of navigation. It is the core type of navigation methods like router.push() / router.replace() / router.back() and the to prop of the RouterLink component.

Type Definition

ts
type RouteLocation =
  | string
  | RouteLocationPath
  | RouteLocationName
  | RouteLocationRaw

Supports multiple forms:

ts
// 1. String path
router.push('/pages/about/about')

// 2. Path object
router.push({ path: 'pages/about/about' })

// 3. Named route
router.push({ name: 'about' })

// 4. With parameters
router.push({ path: 'pages/detail/detail', query: { id: '1' } })
router.push({ name: 'detail', params: { info: { id: 1 } } })

Path Forms

String Path

The simplest form, directly passing a path string:

ts
// Basic path
router.push('/pages/about/about')

// With query (auto-parsed)
router.push('/pages/detail/detail?id=1&name=Tom')

// Named route name (not recommended, use name form instead)
router.push('about')

String path limitations

  • Path should start with / (auto-completed)
  • Cannot pass params (only query strings)
  • Cannot specify advanced options like animation / events / mode
  • Recommend using object form for full capabilities

RouteLocationPath

Specify the target path via path:

ts
interface RouteLocationPath {
  path: string
  query?: Record<string, any>
  params?: Record<string, any>          // (requires ParamsPlugin)
  hash?: string
  animation?: NavigationAnimation       // (requires AnimationPlugin)
  events?: Record<string, Function>     // (requires ChannelPlugin)
  mode?: NavigationMode
  persistent?: boolean                  // (requires ParamsPlugin)
}
ts
await router.push({
  path: 'pages/detail/detail',
  query: { id: '1', tab: 'info' },
  animation: { type: 'slide-in-right' },
  events: {
    onSelectAddress(data) {
      console.log('Address selected:', data)
    }
  }
})

RouteLocationName

Specify a named route via name:

ts
interface RouteLocationName {
  name: string
  query?: Record<string, any>
  params?: Record<string, any>          // (requires ParamsPlugin)
  hash?: string
  animation?: NavigationAnimation       // (requires AnimationPlugin)
  events?: Record<string, Function>     // (requires ChannelPlugin)
  mode?: NavigationMode
  persistent?: boolean                  // (requires ParamsPlugin)
}
ts
await router.push({
  name: 'detail',
  query: { id: '1' },
  params: { info: { id: 1, name: 'Tom' } }
})

Recommend using named routes

  • Decoupling: Path changes don't affect calling code
  • Type safety: With the dts feature of @meng-xi/vite-plugin, route names have type hints
  • Readability: { name: 'detail' } is clearer than '/pages/detail/detail'

Property Details

Plugin dependencies

The params, persistent, animation, and events field types are always available (providing complete type hints), but at runtime they require the corresponding plugin to be registered to take effect. Using them without registering the plugin will throw a PLUGIN_REQUIRED error. See Plugin System for details.

path / name

Choose one to specify the target route. name takes priority over path.

query

  • Type: Record<string, any>
  • Description: URL query parameters, serialized to strings and appended to the URL

query serialization limitations

Values in query are serialized to strings via encodeURIComponent:

  • Simple types (string / number / boolean) pass through normally
  • Complex objects are serialized to [object Object], cannot be restored
  • Arrays are serialized to comma-separated strings

Use params to pass complex objects.

ts
// ✅ Simple types
router.push({ name: 'detail', query: { id: 123, tab: 'info' } })
// URL: /pages/detail/detail?id=123&tab=info

// ❌ Complex object (cannot be restored)
router.push({ name: 'detail', query: { user: { name: 'Tom' } } })
// URL: /pages/detail/detail?user=%5Bobject%20Object%5D
// Receiver: route.value.query.user === '[object Object]'

// ✅ Array (serialized to string)
router.push({ name: 'detail', query: { ids: [1, 2, 3] } })
// URL: /pages/detail/detail?ids=1%2C2%2C3
// Receiver: route.value.query.ids === '1,2,3' (string)

params

  • Type: Record<string, any>
  • Description: Page parameters, supports any serializable data (objects, arrays, nested structures)
  • Storage:
    • Default in-memory (persistent: false)
    • When persistent: true is enabled, persisted via uni.setStorageSync
ts
// Pass complex object
await router.push({
  name: 'detail',
  params: {
    user: { name: 'Tom', age: 20 },
    tags: ['a', 'b', 'c'],
    meta: { source: 'home', timestamp: Date.now() }
  }
})

// Receiver
const route = useRoute()
const user = route.value.params.user  // { name: 'Tom', age: 20 }
const tags = route.value.params.tags  // ['a', 'b', 'c']

params vs query

Featurequeryparams
Type restrictionSimple types onlyAny serializable data
URL visibleYesNo
H5 refresh preservedYesOnly with persistent: true
Size limitURL length limitstorage capacity
Use casesSimple params, shareableComplex data, page communication

hash

  • Type: string
  • Description: URL hash fragment (H5 only)
ts
router.push({ path: '/pages/about/about', hash: '#section-1' })
// URL: /pages/about/about#section-1

Platform limitations

hash is only supported on H5. Mini-programs and App will ignore this parameter.

animation

  • Type: NavigationAnimation
  • Description: Animation config for this navigation (App only), overrides meta.animation
ts
await router.push({
  name: 'detail',
  animation: { type: 'slide-in-bottom', duration: 500 }
})

events

  • Type: Record<string, Function>
  • Description: EventChannel event listeners, for target page to source page communication
ts
// Source page
await router.push({
  name: 'select-address',
  events: {
    onAddressSelected(address) {
      console.log('Received address:', address)
      // Update page data
      form.value.address = address
    }
  }
})

// Target page
const eventChannel = getCurrentInstance()?.proxy?.getOpenerEventChannel?.()
eventChannel?.emit('onAddressSelected', { city: 'Beijing', detail: 'Chaoyang' })

EventChannel advantages

  • Cross-page communication without global state or event bus
  • Auto cleanup: events auto-invalidate when source page closes
  • Type safety: can define event types with TS

mode

  • Type: 'push' | 'replace' | 'relaunch'
  • Description: Navigation mode (v1.7.0+), overrides default behavior
ts
// Force replace (don't keep current page in stack)
await router.push({ name: 'login', mode: 'replace' })

// Restart app (clear page stack)
await router.push({ name: 'home', mode: 'relaunch' })

mode use cases

  • push (default): Regular navigation, keep current page
  • replace: Post-login redirect, post-form-submit redirect, avoid going back to form page
  • relaunch: Logout, user switch, return to home, clear all history

persistent

  • Type: boolean
  • Description: Whether to persist params, overrides global paramsPersistent config
ts
// Global paramsPersistent: false, persist per navigation
await router.push({
  name: 'detail',
  params: { id: 123 },
  persistent: true  // Persist this time
})

// Global paramsPersistent: true, don't persist per navigation
await router.push({
  name: 'detail',
  params: { id: 123 },
  persistent: false  // Don't persist this time
})

RouteLocationNormalized

After navigation completes, the route object stores the normalized route location:

ts
interface RouteLocationNormalized {
  path: string
  name?: string
  query: Record<string, string>
  params: Record<string, any>
  hash: string
  fullPath: string
  meta: RouteMeta
  matched: RouteConfig[]
  redirectedFrom?: RouteLocationNormalized
  _synced?: boolean
}

Special Fields

fullPath

Full path, including query and hash:

ts
const route = useRoute()
console.log(route.value.fullPath)
// /pages/detail/detail?id=1&tab=info#section-1

matched

Matched route config chain (multiple for nested routes):

ts
const route = useRoute()
route.value.matched.forEach((config, index) => {
  console.log(`Match level ${index}:`, config.path)
})

redirectedFrom

If this navigation was triggered by a guard redirect, records the original target:

ts
router.beforeEach((to, from, next) => {
  if (to.meta.requireAuth && !isLoggedIn()) {
    next({ name: 'login' })
  } else {
    next()
  }
})

router.afterEach((to) => {
  if (to.redirectedFrom) {
    console.log(`Redirected from ${to.redirectedFrom.fullPath} to ${to.fullPath}`)
  }
})

_synced

Marks whether this route change is a "state sync" (not a complete navigation):

ts
router.onRouteChange((to, from) => {
  if (to._synced) {
    // State sync: e.g., physical back button, TabBar click
    console.log('State sync, not active navigation')
  } else {
    // Complete navigation: push / replace / back, etc.
    console.log('Complete navigation')
  }
})

_synced applications

  • When physical back button is triggered, route state syncs but _synced is true
  • When TabBar click switches, _synced is also true
  • Can be used to distinguish active navigation from passive state changes, deciding whether to perform certain side effects

Type Parsing Methods

RouteLocationNormalized provides a series of type-parsing methods to conveniently get typed data from query / params:

queryInt / paramInt

Parse a parameter as an integer:

ts
const route = useRoute()

// Parse from query
const id = route.value.queryInt('id')        // 123
const invalid = route.value.queryInt('name')  // NaN (returns NaN when parsing fails)

// Parse from params
const count = route.value.paramInt('count')   // 10

queryNumber / paramNumber

Parse a parameter as a number (supports floats):

ts
const route = useRoute()

const price = route.value.queryNumber('price')    // 99.99
const discount = route.value.paramNumber('discount')  // 0.85

queryBool / paramBool

Parse a parameter as a boolean:

ts
const route = useRoute()

// Supported truthy values: 'true' / '1' / 1 / true
// Supported falsy values: 'false' / '0' / 0 / false / undefined / null
const enabled = route.value.queryBool('enabled')   // true
const debug = route.value.paramBool('debug')       // false

queryJSON / paramJSON

Parse a parameter as a JSON object:

ts
const route = useRoute()

// JSON string in query
const filter = route.value.queryJSON('filter')
// query: ?filter={"category":"book","price":{"min":10,"max":100}}
// After parsing: { category: 'book', price: { min: 10, max: 100 } }

// JSON string in params
const config = route.value.paramJSON('config')

Practical Example

vue
<script setup lang="ts">
import { useRoute } from '@meng-xi/uni-router'

const route = useRoute()

// Parse multiple types from query
const id = route.value.queryInt('id')              // Integer ID
const price = route.value.queryNumber('price')     // Float price
const enabled = route.value.queryBool('enabled')   // Boolean switch
const filter = route.value.queryJSON('filter')     // Complex object

// Parse from params
const user = route.value.paramJSON('user')         // User object
const tags = route.value.paramJSON('tags')         // Tag array

// Safe access (with defaults)
const page = route.value.queryInt('page') || 1
const size = route.value.queryInt('size') || 20
</script>

RouteLocationRaw

Raw route location, the parameter type accepted by navigation methods:

ts
type RouteLocationRaw = string | RouteLocationPathRaw | RouteLocationNamedRaw

The return type of push(), extends RouteLocation with an additional eventChannel for page communication:

ts
interface NavigationResult extends RouteLocation {
  eventChannel?: EventChannel
}
  • eventChannel: Page communication event channel. Available by default in push mode; replace / relaunch return undefined for this field by default. With useUniEventChannel: true enabled, all navigation methods return an available eventChannel.

INFO

NavigationResult extends RouteLocation, so existing code like const route = await router.push(...) works without modification.

EventChannel

Page communication event channel, corresponding to uni-app's native uni.navigateTo EventChannel mechanism:

ts
interface EventChannel {
  emit(event: string, ...args: any[]): EventChannel
  on(event: string, callback: (...args: any[]) => void): EventChannel
  once(event: string, callback: (...args: any[]) => void): EventChannel
  off(event: string, callback?: (...args: any[]) => void): EventChannel
}
  • emit: Send an event to the peer page
  • on: Listen for an event from the peer page
  • once: Listen for an event from the peer page (fires only once)
  • off: Unlisten an event

TIP

All methods return the EventChannel instance, supporting method chaining.

Complete Examples

Basic Navigation

ts
// String path
await router.push('/pages/about/about')

// Named route
await router.push({ name: 'about' })

// With parameters
await router.push({ name: 'detail', query: { id: '1' } })

Complex Data Passing

ts
// Pass complex object
await router.push({
  name: 'detail',
  params: {
    user: { name: 'Tom', age: 20 },
    items: [
      { id: 1, name: 'Product A', price: 99.9 },
      { id: 2, name: 'Product B', price: 199.9 }
    ]
  },
  persistent: true  // Persist, still readable after H5 refresh
})

Cross-Page Communication

ts
// Source page: open selector and listen for result
await router.push({
  name: 'select-address',
  events: {
    onSelected(address) {
      form.value.address = address
    },
    onCancel() {
      console.log('User cancelled selection')
    }
  }
})

// Target page: send result
const instance = getCurrentInstance()
const channel = instance?.proxy?.getOpenerEventChannel?.()

function selectAddress(address) {
  channel?.emit('onSelected', address)
  router.back()
}

function cancel() {
  channel?.emit('onCancel')
  router.back()
}

Custom Animation

ts
// Slide in from bottom (common for detail pages)
await router.push({
  name: 'detail',
  animation: { type: 'slide-in-bottom', duration: 300 }
})

// Fade in (for modal-style pages)
await router.push({
  name: 'modal',
  animation: { type: 'fade-in', duration: 200 }
})

// Reverse animation on back
await router.back({
  animation: { type: 'slide-out-bottom', duration: 300 }
})
ts
// Replace login page after successful login (avoid going back to login)
await router.push({ name: 'home', mode: 'replace' })

// Restart app after logout
await router.push({ name: 'login', mode: 'relaunch' })

// Specify mode when redirecting in guards
router.beforeEach((to, from, next) => {
  if (to.meta.requireAuth && !isLoggedIn()) {
    next({ name: 'login' }, { mode: 'replace' })  // Replace, avoid going back to protected page
  } else {
    next()
  }
})

Next Steps

Released under the MIT License.