skip to content

Vue Router

Vue's official router maps URLs to nested component trees, guards every navigation and, since v5, generates typed routes from files. Interviewers probe guard order and param reuse.

on this pageshow

explore

questions

page 1 of 2

In Vue Router 5 file-based routing, how do src/pages files become routes, and what do index.vue, [slug].vue and [...path].vue generate?

level: juniorimportance: must knowfreq 40%

answer

  1. a bundler plugin scans a folder
  2. src/pages is the default folder
  3. routes arrive from a virtual module
  4. index is the empty segment
  5. brackets and three dots

basics

~10 s

The vue-router/vite plugin scans src/pages and generates the route records, which you import from vue-router/auto-routes. index.vue maps to its folder's path, [slug].vue to a :slug param, and [...path].vue to the catch-all /:path(.*).

solid answer

~30 s

In Vue Router 5 you add `VueRouter()` from `vue-router/vite` to the Vite plugins, before the Vue plugin, and it scans `src/pages` for `.vue` files. You import `routes` from `vue-router/auto-routes` and pass them to `createRouter()` instead of writing the array by hand. The path comes from the file path: `src/pages/index.vue` is `/`, `posts/index.vue` is `/posts`, `posts/[slug].vue` is `/posts/:slug` with `slug` as a param, and `[...path].vue` becomes `/:path(.*)`, which matches anything and serves as the 404 page. Pages are lazy-loaded by default (`importMode: 'async'`), and each generated route with a component is named after its file, such as `/posts/[slug]`.

code

ts · 11 lines
ts
// vite.config.ts
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueRouter from 'vue-router/vite'

export default defineConfig({
  plugins: [
    VueRouter(), // scans src/pages by default
    Vue(),       // must come after VueRouter()
  ],
})

go deeper

for a junior

Recall the three file shapes: index.vue for the folder's own path, [slug].vue for a param, [...path].vue for a catch-all, and that routes come from vue-router/auto-routes.

for a middle

Explain what the plugin generates: lazily imported route records named after their file paths, a types file when TypeScript is present, and why the plugin sits before the Vue plugin.

for a senior

Show you know the plugin is built into Vue Router 5, and that file-derived names such as /posts/[slug] change every named navigation when a project adopts it.

for a principal

Frame the adoption cost: file names become the URL contract, so renaming a folder is a URL change that needs redirects and review, not a silent refactor.

## What file-based routing is in Vue Router 5 A **route table** is the list of route records the router matches URLs against. In a classic setup you write it by hand: an array of objects with `path`, `name` and `component`, passed to `createRouter()`. **File-based routing** derives that array from a folder instead. Since Vue Router 5.0 the plugin that does this ships inside the `vue-router` package; before that it was the separate `unplugin-vue-router` package, whose import paths are now only a migration starting point. The plugin runs inside the bundler. It scans the **routes folder** (`src/pages` by default), turns every `.vue` file it finds into a route record, and serves the result as the module `vue-router/auto-routes`. When TypeScript is installed it also writes a declaration file describing every route, which is what makes navigation type-checked. ## Wiring it up 1. Add `VueRouter()` from `vue-router/vite` to the Vite `plugins` array, **before** the Vue plugin; the docs require that order. 2. Import `routes` from `vue-router/auto-routes` and pass it to `createRouter()` together with a history. 3. Optionally call `handleHotUpdate(router)` under `import.meta.hot`, so adding a page updates the route table without a full reload. 4. Start the dev server once so the plugin writes the first version of the types file. ```ts // src/router.ts import { createRouter, createWebHistory } from 'vue-router' import { routes, handleHotUpdate } from 'vue-router/auto-routes' export const router = createRouter({ history: createWebHistory(), routes, }) if (import.meta.hot) { handleHotUpdate(router) } ``` ## From file name to URL For a blog, a folder like this is typical: | File | Generated path | Notes | |---|---|---| | `src/pages/index.vue` | `/` | `index` is an empty segment, like `index.html` | | `src/pages/about.vue` | `/about` | a static segment | | `src/pages/posts/index.vue` | `/posts` | the folder's own page | | `src/pages/posts/[slug].vue` | `/posts/:slug` | `slug` becomes a route param | | `src/pages/tags/[[tag]].vue` | `/tags/:tag?` | double brackets make the param optional | | `src/pages/[...path].vue` | `/:path(.*)` | a catch-all, typically the 404 page | A few rules follow from the table: - **Brackets mark params.** `[slug]` becomes `:slug`; a param can sit inside a segment (`users_[id].vue` gives `/users_:id`) and one file name can carry several. - **`index` must be lowercase.** The docs call this out; the lowercase `index.vue` is what produces the empty path. - **Three dots make a catch-all.** `[...path].vue` matches any remaining path, so it serves as the not-found page; inside a folder (`posts/[...path].vue` gives `/posts/:path(.*)`) it catches only unknown URLs under `/posts`. - **A trailing `+` makes a param repeatable**: `[slugs]+.vue` becomes `:slugs+`. What those runtime patterns then match, and how the router ranks them against each other, is ordinary Vue Router matching; the file names are only another way of writing the same paths. ## What else the plugin decides for you - **Names.** Every generated route that has a component gets a `name` derived from its file path: `posts/[slug].vue` is named `/posts/[slug]`, the root `index.vue` is `/`. You can override one page's name, or replace the naming function with the `getRouteName` option. - **Lazy loading.** The default `importMode` is `'async'`, so each page component is a dynamic `import()` and lands in its own chunk; `'sync'` imports pages statically. - **Types.** With TypeScript installed, the plugin writes a declaration file listing every route name, path and param. By default it is `typed-router.d.ts` at the project root; the migration guide recommends `dts: 'src/route-map.d.ts'`. - **Watching.** During development the plugin watches the routes folder, so a new file becomes a route without restarting; the `watch` option defaults to on unless the `CI` environment variable is set. - **Which files count.** Only `.vue` files are pages by default; the `extensions` option can add others, such as `.md` for Markdown pages. - **More than one folder.** `routesFolder` also accepts an array, each entry with its own `src` and an optional URL prefix in `path`, for example an admin area served under `admin/`. ## Mistakes juniors make - Keeping a hand-written `routes` array next to the plugin and wondering why new pages never appear: the generated routes reach the router only through `vue-router/auto-routes`. - Expecting the URL `/posts/[slug]`: the brackets are file syntax, while the URL carries the param value, as in `/posts/hello-world`. - Installing `unplugin-vue-router` on a Vue Router 5 project: the plugin is built in, and its old `unplugin-vue-router/vite` path is replaced by `vue-router/vite`. - Naming the home page `Home.vue` and expecting `/`: only `index.vue` maps to the folder's own path.

  • Where does a blog's 404 page go, and can one section have its own?
    `src/pages/[...path].vue` becomes `/:path(.*)` and matches any URL no other route claims, so it serves as the global not-found page. The same file inside a folder, `src/pages/posts/[...path].vue`, becomes `/posts/:path(.*)` and only catches unknown URLs under `/posts`, which lets a section show its own not-found view.
  • Can a file name hold an optional or repeatable param, or more than one param?
    Yes. Double brackets make a param optional (`[[tag]].vue` gives `:tag?`), a trailing `+` makes it repeatable (`[slugs]+.vue` gives `:slugs+`), and both together give `:slugs*`. A param can sit inside a segment, as in `users_[id].vue`, and one file name can carry several params, as in `product_[skuId]_[seoDescription].vue`.
  • What does handleHotUpdate from vue-router/auto-routes do?
    Called as `handleHotUpdate(router)` inside an `if (import.meta.hot)` block, it replaces the router's routes when the plugin regenerates them, so adding, renaming or deleting a page during development takes effect without a full page reload.

The routes folder works like a building directory printed from the doors that actually exist: fit a new door (add a file) and the directory gains an entry, and the sign on the door (the file name, brackets and all) decides the address visitors use.

saying these in an interview costs you the question

  • File-based routing needs Nuxt; plain Vue Router has no such plugin.
  • Vue Router 5 still needs the separate unplugin-vue-router package installed.
  • posts/[slug].vue serves the literal URL /posts/[slug].
  • Every page is bundled into the entry chunk unless you add dynamic imports yourself.
  • The order of VueRouter() and Vue() in vite.config does not matter.
open as a page

In Vue Router 5, how do you write a `router.beforeEach` guard that uses `to.meta.requiresAuth` to send signed-out HR-portal users to `/login` with their destination?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Register router.beforeEach((to) => …) and, when to.meta.requiresAuth is set and nobody is signed in, return { name: 'login', query: { redirect: to.fullPath } }. Returning nothing lets every other navigation through; keep the login route itself unprotected.

open as a page

In Vue Router 5, how do you lazy-load a dashboard route's component, and what breaks if you write `component: import('./Billing.vue')` without the arrow function?

level: juniorimportance: must knowfreq 65%

basics

~20 s

Give the route a loader, component: () => import('./Billing.vue'); the router calls it on first entry, waits for it and caches the result. Without the arrow the import runs at startup, and development warns that production will break.

open as a page

In Vue Router 5, how does a route record with path '/products/:id' match URLs, and how does a script setup component read the id?

level: juniorimportance: must knowfreq 66%

basics

~10 s

The :id segment matches exactly one non-empty path segment, so /products/42 matches and /products/42/reviews does not; a component reads it as useRoute().params.id, a decoded string.

open as a page

In Vue Router 5, how do you build a /settings area whose sidebar layout stays on screen while /settings/profile and /settings/security swap inside it?

level: juniorimportance: must knowfreq 64%

basics

~10 s

Give the /settings record a SettingsLayout component and a children array with relative paths like 'profile'; SettingsLayout renders the sidebar plus its own RouterView, which shows the matched child while the layout stays mounted.

open as a page

In Vue Router 5, how do createWebHistory, createWebHashHistory and createMemoryHistory differ, and when would you pick each one?

level: juniorimportance: must knowfreq 62%

basics

~10 s

createWebHistory gives clean URLs but needs a server fallback to index.html; createWebHashHistory keeps the route after #, needing no server setup but hurting SEO; createMemoryHistory ignores the URL and suits SSR and tests.

open as a page

In Vue Router 5, what happens to a navigation when a `beforeEach` guard returns nothing, returns a route location, returns `false`, or throws?

level: middleimportance: must knowfreq 66%

basics

~20 s

Nothing or true lets the navigation continue to the next guard; a route location drops it and starts a new navigation there; false cancels it and resets the URL to from; a thrown Error cancels it and calls router.onError() handlers.

open as a page

In Vue Router 5, clicking from /products/1 to /products/2 keeps showing product 1; why, and how do you fix the component?

level: middleimportance: must knowfreq 72%

basics

~10 s

Both URLs match the same record, so RouterView patches the existing component instead of remounting it; setup and onMounted do not run again. Watch route.params.id, or the prop from props: true, with immediate: true.

open as a page

On a Vue Router 5 job-search page with filters in the query, how do you change one filter via `router.push` without dropping the others?

level: middleimportance: must knowfreq 62%

basics

~20 s

Copy the current query into the new location: router.push({ query: { ...route.query, remote: 'true', page: undefined } }). A location with only query stays on the current route, but its query replaces the old one, so omitted keys vanish.

open as a page

In Vue Router 5, why must Transition and KeepAlive go inside RouterView's v-slot instead of wrapping RouterView, and what does the slot expose?

level: middleimportance: must knowfreq 55%

basics

~10 s

Transition and KeepAlive must act on the route component, not on RouterView; the v-slot passes that component as Component, plus the displayed route, and you render it with <component :is> inside them.

open as a page

In Vue Router 5 file routing, what can definePage() set on a page's route, and why can't it read variables from <script setup>?

level: middleimportance: should knowfreq 26%

basics

~20 s

definePage() is a build-time macro that overrides a page's route record: name, path, alias, meta and other record fields. The plugin extracts its argument and removes it from the component, so it cannot reference bindings declared in script setup.

open as a page

In Vue Router 5 file routing, what does adding src/pages/posts.vue beside a posts/ folder change, and how do you keep a page out of it?

level: middleimportance: should knowfreq 30%

basics

~20 s

A .vue file with the same name as a folder becomes the parent route, and the folder's pages render inside its RouterView. To add /posts/new outside that layout, name the file posts.new.vue; the dot becomes a slash without nesting.

open as a page

In Vue Router 5, why does `onBeforeRouteLeave` guard an employee edit form's unsaved changes on leaving, but not when switching to another employee?

level: middleimportance: should knowfreq 55%

basics

~20 s

onBeforeRouteLeave runs only when the route record rendering the form is left. /employees/7/edit to /employees/8/edit keeps the same record and component instance, so only onBeforeRouteUpdate runs; register the same check in both and return false to stay.

open as a page

In Vue Router 5, why should a dashboard route use `component: () => import('./Analytics.vue')` instead of wrapping it in `defineAsyncComponent()`?

level: middleimportance: should knowfreq 45%

basics

~20 s

A plain loader lets the router load the page's code before confirming and run its guards. defineAsyncComponent hands over a ready-looking wrapper: the router confirms at once, misses the page's Options API guards, and warns in development.

open as a page

In Vue Router 5, what do a route record's props: true, object and function modes pass to the component, and when does each fit?

level: middleimportance: should knowfreq 48%

basics

~10 s

props: true passes route.params as props, an object passes fixed props as-is, and a function receives the route and returns props, which is how you cast ids to numbers or pass query values.

open as a page

In Vue Router 5, how do a docs route '/docs/:path+' and a '/:pathMatch(.*)*' 404 route match multi-segment URLs, and what params do they produce?

level: middleimportance: should knowfreq 55%

basics

~10 s

A + param repeats over one or more segments and yields an array, so /docs/guide/setup gives ['guide', 'setup']; /:pathMatch(.) matches any path, ranks below specific routes, and yields the segments as an array.

open as a page

In Vue Router 5, why does every page-number `<RouterLink>` on a job-search results page get `router-link-exact-active`, and how do you highlight only the current page?

level: middleimportance: should knowfreq 48%

basics

~20 s

RouterLink's active matching compares the route record and params only, never the query, so every ?page=N link on the same route is active and exact-active. Compare route.query.page yourself through custom with its slot, or through useLink.

open as a page

In Vue Router 5, why does push({ name: 'settings' }) show an empty settings pane while reloading /settings shows the overview child?

level: middleimportance: should knowfreq 42%

basics

~20 s

The name sits on the parent, so a named push matches only the parent and its RouterView stays empty, while a path match prefers the empty-path child. Put the name on the empty-path child instead.

open as a page

In Vue Router 5, how do named views show a mail list and message detail side by side, and how do they differ from nesting?

level: middleimportance: should knowfreq 38%

basics

~20 s

A record's components map (default, detail, ...) fills several RouterViews at the same depth, each selected by its name prop; nested routes instead stack one view inside another. Views without a component for the route render nothing.

open as a page

In Vue Router 5, what does app.use(router) start, and why would you await router.isReady() before calling app.mount()?

level: middleimportance: should knowfreq 50%

basics

~20 s

app.use(router) registers RouterLink and RouterView, exposes $router and $route, and in the browser starts the asynchronous first navigation; awaiting isReady() mounts only after it finishes, avoiding an empty first render and a spurious enter transition.

open as a page

In Vue Router 5, why do unit tests and SSR server entries use createMemoryHistory, and what must happen before the first render?

level: middleimportance: should knowfreq 33%

basics

~10 s

createMemoryHistory keeps locations in memory, so the server needs no window and tests share no URL; push the starting route and await router.isReady() before rendering, because on the server nothing starts the first navigation.

open as a page

In Vue Router 5, a Vite-built admin SPA is served by nginx under /admin/; how do you set the router base, route paths and fallback?

level: middleimportance: should knowfreq 44%

basics

~10 s

Set Vite's base to /admin/, pass import.meta.env.BASE_URL to createWebHistory(), keep route paths app-relative like /users, and make nginx fall back to /admin/index.html for unknown paths under /admin/.

open as a page

Migrating a blog's hand-written Vue Router routes array to Vue Router 5's src/pages file routing, what breaks first, and how do you move named routes, redirects and beforeEnter guards?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Route names change first: generated names like /posts/[slug] break every push by the old name. Keep names with definePage({ name }) or update callers, append redirects to the generated routes array, and turn beforeEnter guards into meta checked by a global guard.

open as a page

In Vue Router 5 file routing, how does the generated RouteNamedMap type-check router.push and useRoute(), and which mistakes does it still let through?

level: seniorimportance: should knowfreq 33%

basics

~20 s

The plugin writes a declaration file whose RouteNamedMap lists each route's name, path and params and plugs it into Vue Router's TypesConfig. Named pushes then check names and params, but string paths, runtime-added routes and a stale file slip through.

open as a page

In Vue Router 5, a legacy `beforeEach((to, from, next) => …)` guard logs a deprecation warning and sometimes leaves a navigation pending forever; why, and how do you migrate it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

A guard declared with a third next parameter is resolved only when next is called, so a branch that forgets it hangs the navigation; next is deprecated in Vue Router 5. Drop the parameter and return the value you passed to next.

open as a page

In Vue Router 5, when a user leaves an HR edit form for a lazily loaded payroll route with `beforeEnter`, in what order do the guards run, and where does `beforeResolve` fit?

level: seniorimportance: should knowfreq 50%

basics

~10 s

Leave guards of the edit form, global beforeEach, update guards of reused components, the payroll record's beforeEnter, the lazy component loads, its beforeRouteEnter, global beforeResolve, confirmation, afterEach, the DOM update, then next(vm) callbacks.

open as a page

In Vue Router 5, a dashboard adds a tenant's feature routes with `router.addRoute()` inside `beforeEach`; why does a deep link to `/reports/q3` still render the 404 page, and what fixes it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

to was resolved before the reports route existed, so it matched the catch-all. After addRoute(), return to.fullPath so the URL is resolved again; returning to re-resolves by the catch-all's name. Add the routes once, or it loops.

open as a page

In a Vue Router 5 multi-tenant dashboard, how do you register, check and remove permission-based feature routes so a tenant switch or logout leaves no stale routes behind?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Add each feature's records with router.addRoute('dashboard', record) and keep the removers it returns, or give records names for router.removeRoute(name). On a tenant switch, navigate to a neutral page first, then remove the old set and add the new one.

open as a page

In Vue Router 5, a catalogue has /products/new, /products/:id(\d+) and /products/:slug; how does the router pick a route, and when does array order matter?

level: seniorimportance: should knowfreq 34%

basics

~10 s

Vue Router ranks records by a per-segment score: static segments beat params, a custom regex beats a plain param, and optional, repeatable and wildcard params score lower. Array order only breaks exact ties.

open as a page

showing 1–30 of 36