Typed routes and param parsers (vue-router@5.3.1)
How types wire up
The public generic types (RouteLocationRaw<Name>, useRoute(), push(), RouterLink to) all read RouteMap, which resolves from TypesConfig['RouteNamedMap'] if augmented, else falls back to a generic map (dist/options-CwYZKRl4.d.mts:88-92).
With file-based routing the generated typed-router.d.ts does the wiring for you:
// generated (excerpt): declares the map on vue-router/auto-routes,
// then points TypesConfig at it
declare module 'vue-router/auto-routes' {
export interface RouteNamedMap { /* RouteRecordInfo per route */ }
}
declare module 'vue-router' {
interface TypesConfig {
RouteNamedMap: import('vue-router/auto-routes').RouteNamedMap
_RouteFileInfoMap: import('vue-router/auto-routes')._RouteFileInfoMap
_ParamParsers: { /* custom parser types */ }
}
}(dist/unplugin-C2XPnbvD.mjs:1661-1672)
Manual setup (no bundler plugin):
// typed-router.d.ts (hand written)
import type { RouteRecordInfo } from 'vue-router'
declare module 'vue-router/auto-routes' {
export interface RouteNamedMap {
'/users/[id]': RouteRecordInfo<'/users/[id]', '/users/:id', { id: string }, { id: string }>
}
}
declare module 'vue-router' {
interface TypesConfig { RouteNamedMap: import('vue-router/auto-routes').RouteNamedMap }
}Augmenting TypesConfig on 'vue-router' (not 'vue-router/auto-routes' alone) is what activates the typed overloads.
What you get
router.push({ name: '/users/[id]', params: { id: '2' } }) // wrong name/param: type error
useRoute().params.id // typed per current page with the sfc-typed-router Volar plugin
route.fullPath // exact literal union without VolarSince v5.1.0 the Volar plugin also narrows typeof useRoute in type contexts and definePage's params.path keys are restricted to the file's actual path params via _RouteFileInfoMap (https://github.com/vuejs/router/releases/tag/v5.1.0, dist/index-D7ja2BKs.d.ts:1624-1679).
Param parsers (experimental)
Enable with experimental.paramParsers on the bundler plugin (true, or { dir, include, exclude }; defaults dir: ['src/params'], include: ['*.ts'], exclude: ['*.test.{ts,js}', '*.spec.{ts,js}'], flat folder only). Source: dist/options-CwYZKRl4.d.mts:2748-2769.
Native parsers: int → number, bool → boolean, string → string (dist/unplugin-C2XPnbvD.mjs:1078-1084).
Use in filenames: src/pages/users/[id=int].vue → route.params.id: number, match fails on non-integers.
Custom parser (file name becomes the parser name, camelCased; src/params/date.ts → date):
// src/params/date.ts
import { defineParamParser, miss } from 'vue-router/experimental'
export const parser = defineParamParser<Date>({
get: value => {
const date = new Date(value)
if (Number.isNaN(date.getTime())) miss(`"${value}" is not a valid date`)
return date
},
set: value => value.toISOString(),
})defineParamParser handles optional (→ null) and repeatable (→ array) params for you (dist/experimental/index.js:737-772). miss() throws a MatchMiss internally so the route falls through to the next match (v5.0.3 made miss() return never, https://github.com/vuejs/router/releases/tag/v5.0.3).
defineParamParserRaw gives full control (you handle arrays/nullish yourself) (dist/experimental/index.js:691-736). Standard Schema validators (valibot, zod, ...) are accepted wherever a parser is expected since v5.0.5 (dist/index-D7ja2BKs.d.ts:1152-1167, https://github.com/vuejs/router/releases/tag/v5.0.5).
Query params via definePage:
definePage({
params: {
query: {
page: { parser: 'int', default: 1 }, // ?page=3 → route.params.page === 3
tags: { parser: 'string', format: 'array' }, // ?tags=a&tags=b → string[]
token: { required: true }, // route only matches if ?token present
},
},
})Since v5, query params in typed routes are optional by default.
Experimental router + auto-resolver
experimental_createRouter replaces the routes array with a fixed resolver generated by the plugin. It has no addRoute/removeRoute (routes are build-time only) (dist/index-D7ja2BKs.d.ts:1578-1587).
import { experimental_createRouter } from 'vue-router/experimental'
import { createWebHistory } from 'vue-router'
import { resolver, handleHotUpdate } from 'vue-router/auto-resolver'
const router = experimental_createRouter({ history: createWebHistory(), resolver })
if (import.meta.hot) handleHotUpdate(router)Requires experimental.paramParsers on the plugin so it emits the resolver module (vue-router-auto-resolver.d.mts:15-21). Types: EXPERIMENTAL_Router, EXPERIMENTAL_RouterOptions, records become EXPERIMENTAL_RouteRecord* with path/query/hash matcher patterns.
Global type overrides (TypesConfig)
Beyond RouteNamedMap, TypesConfig slots (marked internal but load-bearing, dist/options-CwYZKRl4.d.mts:8-23):
Router— swap the publicRoutertype (v5.1.0 feature), also affectsuseRouter()return.Error(on'vue-router/experimental') — error type of data loaders (dist/index-D7ja2BKs.d.ts:1791-1809)._ParamParsers,_RouteFileInfoMap— written by codegen; do not hand-edit unless you own the generation.