[ICO]NameLast modifiedSizeDescription
[PARENTDIR]Parent Directory  -  
[DIR]dist/2024-10-02 08:35 -  
[DIR]node_modules/2024-10-02 08:35 -  
[   ]LICENSE2024-10-02 08:35 1.1K 
[TXT]README.md2024-10-02 08:35 20K 
[TXT]client.d.ts2024-10-02 08:35 814  
[TXT]esbuild.d.ts2024-10-02 08:35 41  
[TXT]index.d.ts2024-10-02 08:35 29  
[TXT]options.d.ts2024-10-02 08:35 31  
[   ]package.json2024-10-02 08:35 3.9K 
[TXT]rollup.d.ts2024-10-02 08:35 40  
[   ]route.schema.json2024-10-02 08:35 699  
[TXT]runtime.d.ts2024-10-02 08:35 31  
[TXT]types.d.ts2024-10-02 08:35 29  
[TXT]vite.d.ts2024-10-02 08:35 38  
[TXT]webpack.d.ts2024-10-02 08:35 41  
# unplugin-vue-router

[![NPM version](https://img.shields.io/npm/v/unplugin-vue-router?color=black&label=)](https://www.npmjs.com/package/unplugin-vue-router) [![ci status](https://github.com/posva/unplugin-vue-router/actions/workflows/ci.yml/badge.svg)](https://github.com/posva/unplugin-vue-router/actions/workflows/ci.yml)

> Automatic file based Routing in Vue with TS support ✨

<!-- https://user-images.githubusercontent.com/664177/176622756-3d10acc6-caac-40ff-a41f-9bdccadf7f1d.mp4 -->

<p align="center">
  <img src="https://user-images.githubusercontent.com/664177/176623167-0153f9fb-79cd-49a7-8575-429ce323dd11.gif" >
</p>

This build-time plugin simplifies your routing setup **and** makes it safer and easier to use thanks to TypeScript. Requires Vue Router at least 4.1.0.

⚠️ This package is still experimental. If you found any issue, design flaw, or have ideas to improve it, please, open an [issue](https://github.com/posva/unplugin-vue-router/issues/new/choose) or a [Discussion](https://github.com/posva/unplugin-vue-router/discussions).

## Install

```bash
npm i -D unplugin-vue-router
```

Add VueRouter plugin **before** Vue plugin:

<details>
<summary>Vite</summary><br>

```ts
// vite.config.ts
import VueRouter from 'unplugin-vue-router/vite'

export default defineConfig({
  plugins: [
    VueRouter({
      /* options */
    }),
    // ⚠️ Vue must be placed after VueRouter()
    Vue(),
  ],
})
```

Example: [`playground/`](./playground/)

<br></details>

<details>
<summary>Rollup</summary><br>

```ts
// rollup.config.js
import VueRouter from 'unplugin-vue-router/rollup'

export default {
  plugins: [
    VueRouter({
      /* options */
    }),
    // ⚠️ Vue must be placed after VueRouter()
    Vue(),
  ],
}
```

<br></details>

<details>
<summary>Webpack</summary><br>

```ts
// webpack.config.js
module.exports = {
  /* ... */
  plugins: [
    require('unplugin-vue-router/webpack')({
      /* options */
    }),
  ],
}
```

<br></details>

<details>
<summary>Vue CLI</summary><br>

```ts
// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      require('unplugin-vue-router/webpack')({
        /* options */
      }),
    ],
  },
}
```

<br></details>

<details>
<summary>esbuild</summary><br>

```ts
// esbuild.config.js
import { build } from 'esbuild'
import VueRouter from 'unplugin-vue-router/esbuild'

build({
  plugins: [VueRouter()],
})
```

<br></details>

## Setup

After installing, **you should run your dev server** (usually `npm run dev`) **to generate the first version of the types**. Then, you should replace your imports from `vue-router` to `vue-router/auto`:

```diff
-import { createRouter, createWebHistory } from 'vue-router'
+import { createRouter, createWebHistory } from 'vue-router/auto'

createRouter({
  history: createWebHistory(),
  // You don't need to pass the routes anymore,
  // the plugin writes it for you 🤖
})
```

> **Note**
> You can exclude `vue-router` from VSCode import suggestions by adding this setting to your `.vscode/settings.json`:

```json
{
  "typescript.preferences.autoImportFileExcludePatterns": ["vue-router"]
}
```

This will ensure VSCode only suggests `vue-router/auto` for imports. Alternatively, you can also configure [auto imports](#auto-imports).

Alternatively, **you can also import the `routes` array** and create the router manually or pass it to some plugin. Here is an example with [Vitesse starter](https://github.com/antfu/vitesse/blob/main/src/main.ts):

<!-- TODO: add notes for data fetching guards -->

```diff
 import { ViteSSG } from 'vite-ssg'
 import { setupLayouts } from 'virtual:generated-layouts'
 import App from './App.vue'
 import type { UserModule } from './types'
-import generatedRoutes from '~pages'
+import { routes } from 'vue-router/auto/routes'

 import '@unocss/reset/tailwind.css'
 import './styles/main.css'
 import 'uno.css'

-const routes = setupLayouts(generatedRoutes)

 // https://github.com/antfu/vite-ssg
 export const createApp = ViteSSG(
   App,
   {
-   routes,
+   routes: setupLayouts(routes),
    base: import.meta.env.BASE_URL
  },
   (ctx) => {
     // install all modules under `modules/`
     Object.values(import.meta.glob<{ install: UserModule }>('./modules/*.ts', { eager: true }))
       .forEach(i => i.install?.(ctx))
   },
 )
```

### Auto Imports

If you are using [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import), make sure to remove the `vue-router` preset and use the one exported by `unplugin-vue-router`:

```diff
 import { defineConfig } from 'vite'
 import AutoImport from 'unplugin-auto-import/vite'
+import { VueRouterAutoImports } from 'unplugin-vue-router'

 export default defineConfig({
   plugins: [
     // other plugins
     AutoImport({
       imports: [
-        'vue-router',
+        VueRouterAutoImports,
       ],
     }),
   ],
 })
```

Note that the `vue-router` preset might export less things than the one exported by `unplugin-vue-router` so you might need to add any other imports you were relying on manually:

```diff
     AutoImport({
       imports: [
-        'vue-router',
+        VueRouterAutoImports,
+        {
+           // add any other imports you were relying on
+           'vue-router/auto': ['useLink']
+        },
       ],
     }),
```

Make sure to also check and follow [the TypeScript section](#typescript) below **if you are using TypeScript or have a `jsconfig.json` file**.

## Configuration

Have a glimpse of all the existing configuration options with their corresponding **default values**:

```ts
VueRouter({
  // Folder(s) to scan for vue components and generate routes. Can be a string, or
  // an object, or an array of those. Each option allows to override global options.
  // like exclude, extensions, etc.
  routesFolder: 'src/pages',

  // allowed extensions for components to be considered as pages
  // can also be a suffix: e.g. `.page.vue` will match `home.page.vue`
  // but remove it from the route path
  extensions: ['.vue'],

  // list of glob files to exclude from the routes generation
  // e.g. ['**/__*'] will exclude all files and folders starting with `__`
  // e.g. ['**/__*/**/*'] will exclude all files within folders starting with `__`
  // e.g. ['**/*.component.vue'] will exclude components ending with `.component.vue`
  exclude: [],

  // Path for the generated types. Defaults to `./typed-router.d.ts` if typescript
  // is installed. Can be disabled by passing `false`.
  dts: './typed-router.d.ts',

  // Override the name generation of routes. unplugin-vue-router exports two versions:
  // `getFileBasedRouteName()` (the default) and `getPascalCaseRouteName()`. Import any
  // of them within your `vite.config.ts` file.
  getRouteName: (routeNode) => myOwnGenerateRouteName(routeNode),

  // Customizes the default langage for `<route>` blocks
  // json5 is just a more permissive version of json
  routeBlockLang: 'json5',

  // Change the import mode of page components. Can be 'async', 'sync', or a function with the following signature:
  // (filepath: string) => 'async' | 'sync'
  importMode: 'async',
})
```

## Routes folder structure

By default, this plugins checks the folder at `src/pages` for any `.vue` files and generates the corresponding routing structure basing itself in the file name. This way, you no longer need to maintain a `routes` array when adding routes to your application, **instead just add the new `.vue` component to the routes folder and let this plugin do the rest!**

Let's take a look at a simple example:

```text
src/pages/
├── index.vue
├── about.vue
└── users/
    ├── index.vue
    └── [id].vue
```

This will generate the following routes:

- `/`: -> renders the `index.vue` component
- `/about`: -> renders the `about.vue` component
- `/users`: -> renders the `users/index.vue` component
- `/users/:id`: -> renders the `users/[id].vue` component. `id` becomes a route param.

### Index Routes

Any `index.vue` file will generate an empty path (similar to `index.html` files):

- `src/pages/index.vue`: generates a `/` route
- `src/pages/users/index.vue`: generates a `/users` route

### Nested Routes

Nested routes are automatically defined by defining a `.vue` file alongside a folder **with the same name**. If you create both a `src/pages/users/index.vue` and a `src/pages/users.vue` components, the `src/pages/users/index.vue` will be rendered within the `src/pages/users.vue`'s `<RouterView>`.

In other words, given this folder structure:

```text
src/pages/
├── users/
│   └── index.vue
└── users.vue
```

You will get this `routes` array:

```js
const routes = [
  {
    path: '/users',
    component: () => import('src/pages/users.vue'),
    children: [
      { path: '', component: () => import('src/pages/users/index.vue') },
    ],
  },
]
```

While omitting the `src/pages/users.vue` component will generate the following routes:

```js
const routes = [
  {
    path: '/users',
    // notice how there is no component here
    children: [
      { path: '', component: () => import('src/pages/users/index.vue') },
    ],
  },
]
```

Note the folder and file's name `users/` could be any valid naming like `my-[id]-param/`.

#### Nested routes without nesting layouts

Sometimes you might want to add _nesting to the URL_ in the form of slashes but you don't want it to impact your UI hierarchy. Consider the following folder structure:

```text
src/pages/
├── users/
│   ├── [id].vue
│   └── index.vue
└── users.vue
```

If you want to add a new route `/users/create` you could add a new file `src/pages/users/create.vue` but that would nest the `create.vue` component within the `users.vue` component. To avoid this you can instead create a file `src/pages/users.create.vue`. The `.` will become a `/` when generating the routes:

```js
const routes = [
  {
    path: '/users',
    component: () => import('src/pages/users.vue'),
    children: [
      { path: '', component: () => import('src/pages/users/index.vue') },
      { path: ':id', component: () => import('src/pages/users/[id].vue') },
    ],
  },
  {
    path: '/users/create',
    component: () => import('src/pages/users.create.vue'),
  },
]
```

### Named routes

All generated routes that have a `component` property will have a `name` property. This avoid accidentally directing your users to a parent route. By default, names are generated using the file path, but you can override this behavior by passing a custom `getRouteName()` function. You will get TypeScript validation almost everywhere, so changing this should be easy.

### Dynamic Routes

You can add [route params](https://router.vuejs.org/guide/essentials/dynamic-matching.html) by wrapping the _param name_ with brackets, e.g. `src/pages/users/[id].vue` will create a route with the following path: `/users/:id`. Note you can add a param in the middle in between static segments: `src/pages/users_[id].vue` -> `/users_:id`. You can even add multiple params: `src/pages/product_[skuId]_[seoDescription].vue`.

You can create [**optional params**](https://router.vuejs.org/guide/essentials/route-matching-syntax.html#optional-parameters) by wrapping the _param name_ with an extra pair of brackets, e.g. `src/pages/users/[[id]].vue` will create a route with the following path: `/users/:id?`.

You can create [**repeatable params**](https://router.vuejs.org/guide/essentials/route-matching-syntax.html#repeatable-params) by adding a plus character (`+`) after the closing bracket, e.g. `src/pages/articles/[slugs]+.vue` will create a route with the following path: `/articles/:slugs+`.

And you can combine both to create optional repeatable params, e.g. `src/pages/articles/[[slugs]]+.vue` will create a route with the following path: `/articles/:slugs*`.

### Catch all / 404 Not found route

To create a catch all route prepend 3 dots (`...`) to the param name, e.g. `src/pages/[...path].vue` will create a route with the following path: `/:path(.*)`. This will match any route. Note this can be done inside a folder too, e.g. `src/pages/articles/[...path].vue` will create a route with the following path: `/articles/:path(.*)`.

### Multiple routes folders

It's possible to provide multiple routes folders by passing an array to `routesFolder`:

```js
VueRouter({
  routesFolder: ['src/pages', 'src/admin/routes'],
})
```

You can also provide a path prefix for each of these folders, it will be used _as is_, and **cannot start with a `/`** but can contain any params you want or even **not finish with a `/`**:

```js
VueRouter({
  routesFolder: [
    'src/pages',
    {
      src: 'src/admin/routes',
      // note there is always a trailing slash and never a leading one
      path: 'admin/',
      // src/admin/routes/dashboard.vue -> /admin/dashboard
    },
    {
      src: 'src/docs',
      // you can add parameters
      path: 'docs/:lang/',
      // src/docs/introduction.vue -> /docs/:lang/introduction
    },
    {
      src: 'src/promos',
      // you can omit the trailing slash
      path: 'promos-',
      // src/promos/black-friday.vue -> /promos-black-friday
    },
  ],
})
```

Note that the provided folders must be separate and one _route folder_ cannot contain another specified _route folder_. If you need further customization, give [definePage()](#definepage-in-script) a try.

## TypeScript

This plugin generates a `d.ts` file with all the typing overrides when the dev or build server is ran. Make sure to include it in your `tsconfig.json`'s (or `jsconfig.json`'s) `include` or `files` property:

```js
{
  // ...
  "include": [/* ... */ "typed-router.d.ts"]
  // ...
}
```

Then, you will be able to import from `vue-router/auto` (instead of `vue-router`) to get access to the typed APIs. You can commit the `typed-router.d.ts` file to your repository to make your life easier.

### Extra types

You can always take a look at the generated `typed-router.d.ts` file to inspect what are the generated types. `unplugin-vue-router` improves upon many of the existing types in `vue-router` and adds a few ones as well:

#### `RouteNamedMap`

The `RouteNamedMap` interface gives you access to all the metadata associated with a route. It can also be extended to enable types for **dynamic routes** that are added during runtime.

```ts
import type { RouteNamedMap } from 'vue-router/auto/routes'
```

Extending types with dynamically added routes:

```ts
declare module 'vue-router/auto/routes' {
  import type {
    RouteRecordInfo,
    ParamValue,
    // these are other param helper types
    ParamValueOneOrMore,
    ParamValueZeroOrMore,
    ParamValueZeroOrOne,
  } from 'unplugin-vue-router'
  export interface RouteNamedMap {
    // the key is the name and should match the first generic of RouteRecordInfo
    'custom-dynamic-name': RouteRecordInfo<
      'custom-dynamic-name',
      '/added-during-runtime/[...path]',
      // these are the raw param types (accept numbers, strings, booleans, etc)
      { path: ParamValue<true> },
      // these are the normalized params as found in useRoute().params
      { path: ParamValue<false> }
    >
  }
}
```

#### `RouterTyped`

The `RouterTyped` type gives you access to the typed version of the router instance. It's also the _ReturnType_ of the `useRouter()` function.

```ts
import type { RouterTyped } from 'vue-router/auto'
```

#### `RouteLocationResolved`

The `RouteLocationResolved` type exposed by `vue-router/auto` allows passing a generic (which autocomplete) to type a route **whenever checking the name doesn't makes sense because you know the type**. This is useful for cases like `<RouterLink v-slot="{ route }">`:

```vue
<RouterLink v-slot="{ route }">
  User {{ (route as RouteLocationResolved<'/users/[id]'>).params.id }}
</RouterLink>
```

This type is also the return type of `router.resolve()`.

You have the same equivalents for `RouteLocation`, `RouteLocationNormalized`, and `RouteLocationNormalizedLoaded`. All of them exist in `vue-router` but `vue-router/auto` override them to provide a type safe version of them. In addition to that, you can pass the name of the route as a generic:

```ts
// these are all valid
let userWithId: RouteLocationNormalizedLoaded<'/users/[id]'> = useRoute()
userWithId = useRoute<'/users/[id]'>()
// 👇 this one is the easiest to write because it autocomplete
userWithId = useRoute('/users/[id]')
```

## Named views

It is possible to define [named views](https://router.vuejs.org/guide/essentials/named-views.html#named-views) by appending an `@` + a name to their filename, e.g. a file named `src/pages/index@aux.vue` will generate a route of:

```js
{
  path: '/',
  component: {
    aux: () => import('src/pages/index@aux.vue')
  }
}
```

Note that by default a non named route is named `default` and that you don't need to name your file `index@default.vue` even if there are other named views (e.g. having `index@aux.vue` and `index.vue` is the same as having `index@aux.vue` and `index@default.vue`).

## Extending existing routes

### `definePage()` in `<script>`

The macro `definePage()` allows you to define any extra properties related to the route. It is useful when you need to customize the `path`, the `name`, `meta`, etc

```vue
<script setup>
import { definePage } from 'vue-router/auto'

definePage({
  name: 'my-own-name',
  path: '/absolute-with-:param',
  alias: ['/a/:param'],
  meta: {
    custom: 'data',
  },
})
</script>
```

Note you cannot use variables in `definePage()` as its passed parameter gets extracted at build time and is removed from `<script setup>`. You can also use [the `<route>` block](#sfc-route-custom-block) which allows other formats like yaml.

### SFC `<route>` custom block

The `<route>` custom block is a way to extend existing routes. It can be used to add new `meta` fields, override the `path`, the `name`, or anything else in a route. **It has to be added to a `.vue` component inside of the [routes folder](#routes-folder-structure)**. It is similar to [the same feature in vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages#sfc-custom-block-for-route-data) to facilitate migration.

```vue
<route lang="json">
{
  "name": "name-override",
  "meta": {
    "requiresAuth": false
  }
}
</route>
```

Note you can specify the language to use with `<route lang="yaml">`. By default, the language is JSON5 (more flexible version of JSON) but yaml and JSON are also supported. **This will also add Syntax Highlighting**.

### `extendRoutes()`

You can extend existing routes by passing an `extendRoutes` function to `createRouter()`. **This should be used as a last resort** (or until a feature is natively available here):

```js
import { createWebHistory, createRouter } from 'vue-router/auto'

const router = createRouter({
  extendRoutes: (routes) => {
    const adminRoute = routes.find((r) => r.name === '/admin')
    if (adminRoute) {
      adminRoute.meta ??= {}
      adminRoute.meta.requiresAuth = true
    }
    // completely optional since we are modifying the routes in place
    return routes
  },
  history: createWebHistory(),
})
```

As this plugin evolves, this function should be used less and less and only become necessary in unique edge cases.

One example of this is using [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) which can only be used alongside `extendRoutes()`:

```ts
import { createRouter } from 'vue-router/auto'
import { setupLayouts } from 'virtual:generated-layouts'

const router = createRouter({
  // ...
  extendRoutes: (routes) => setupLayouts(routes),
})
```

## Rationale

This project idea came from trying [to type the router directly using Typescript](https://github.com/vuejs/router/pull/1397/commits/a7c591b6fd5d8478ba3f87e833514bc0e30f93a9), finding out it's not fast enough to be pleasant to use and, ending up using build-based tools, taking some inspiration from other projects like:

- [Nuxt](https://nuxtjs.org/) - The Vue.js Framework
- [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages) - Framework agnostic file based routing
- [Typed Router for Nuxt](https://github.com/victorgarciaesgi/nuxt-typed-router) - A module to add typed routing to Nuxt

## License

[MIT](http://opensource.org/licenses/MIT)