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?
answer
- a bundler plugin scans a folder
- src/pages is the default folder
- routes arrive from a virtual module
- index is the empty segment
- brackets and three dots
basics
~10 sThe 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 sIn 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// 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
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.
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.
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.
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.