@onoxm/vite-plugin-auto-router

a vite plugin for automatically generating react or vue route files
12,808
Install
npm install @onoxm/vite-plugin-auto-router
Latest Version:0.11.0
License:MIT
Last Updated:Oct 20, 2026
Links: Gitee  ·  npm
Maintainer: onoxm

@onoxm/vite-plugin-auto-router

A Vite plugin for automatically generating React or Vue route files.

English | 中文

✨ Features

  • Auto-generate route configuration, no manual maintenance needed
  • Supports both React and Vue frameworks
  • Convention-based routing, automatically mapped by directory structure
  • Supports dynamic route [id] syntax
  • Route groups: wrap a directory name in parentheses to organize pages without adding a URL segment
  • Directory layouts: a __layout__ file turns a directory into a shell wrapping its subtree while preserving the index layer (recommended over type: 'wrap')
  • home page path automatically converted to / (configurable)
  • __root__ page as root route container, wrapping all other routes (configurable) — deprecated, prefer root-level __layout__
  • Zero-config hot module replacement
  • Supports page-level configuration files
  • Virtual module only: route code is always served in-memory via virtual:onoxm-auto-router/{react|vue} and never written to disk — no generated artifacts are left in your project
  • Auto-injects vite-env.d.ts type declarations, out-of-the-box TypeScript support
  • Full IntelliSense: every plugin option ships with English JSDoc (including defaults) in the published type declarations — just hover in your IDE
  • TypeScript type safety

🚀 Installation

npm install -D @onoxm/vite-plugin-auto-router

🔄 Migrating from v0.9.x

v0.10.0 is a cleanup release. The following options have been removed — please delete them from your vite.config.ts:

Removed option What to do
virtualModule No longer needed. Virtual module is now the only output mode.
routesFile No longer needed. Routes are never written to disk.
hmr No longer needed. HMR is enabled by default.
hmrDebounceMs No longer needed.
dryRun Use log: 'tree' or log: 'json' for preview instead.
onGenerated No longer needed.

Before (v0.9.x):

autoRouter({
  virtualModule: true,
  hmr: true,
  dryRun: false
})

After (v0.10.0):

autoRouter() // If you don't need other options
// Or
autoRouter({ log: 'tree' }) // If you want a preview

📖 Usage Guide

Page Component Identification Rules

The plugin identifies page components based on the following rules:

React Project
  • Page components: Components with default export (export default) in the pages directory
  • Regular components: Components with named export (export const) in the pages directory
Vue Project
  • Page components:
    • Direct child components in the views directory (e.g., src/views/about.vue)
    • In nested directories, only index.vue is considered a page component (e.g., src/views/a/b/index.vue)
  • Regular components: Non-index.vue files in nested directories (e.g., src/views/a/b.vue)

Ignored Directories

The following directory names (plus any directory starting with .) are always skipped while scanning pagesDir — pages inside them never become routes:

Category Directory names
Dependencies / VCS node_modules, .git, .hg, .svn, .github, .vscode, .idea
Build output / test folders dist, build, out, .next, .nuxt, .cache, .turbo, .vite, test, tests, __tests__, __test__

The second group has nothing to do with pages, so skipping one emits a warning listing the skipped directories (each directory is reported once). Obviously-ignored names such as node_modules stay silent.

The name-based rule cannot be turned off. If you really need pages under a directory called test or build, rename it (e.g. views/acceptance/).

Route Groups (group)

A directory whose name is wrapped in parentheses is a route group. It is purely organizational and does not occupy a URL segment, so you can group pages without changing their final paths:

src/pages/
├── (marketing)/
│   ├── pricing.tsx        → /pricing
│   └── contact.tsx        → /contact
└── order/
    └── (detail)/
        └── [id].tsx       → /order/:id
  • (marketing)/pricing.tsx and a top-level pricing.tsx produce exactly the same route — same final path, same position in the tree.
  • Groups can be nested and may appear at any level: the group segments are stripped first, then the remaining segments form the URL.
  • Because the segment is stripped, a group is transparent to duplicate-path detection: (a)/x and (b)/x both resolve to /x and therefore collide (the plugin throws a duplicate-path error).

⚠️ 404 / notfound must not live inside a group

(marketing)/404.tsx resolves to the not-found path just like a top-level 404.tsx, but a 404 page inside a group is almost always a mistake. The plugin throws an error instead of silently treating it as the global 404 (or clashing with an existing one). Move it to the pages root. This rule applies only to group directories — a normal subdirectory such as pages/user/404.tsx keeps its current behavior.

Group membership is surfaced in the preview only (log: 'tree' appends [group: marketing]; log: 'json' adds a group field). It is internal metadata and is never written into the generated routes array.

A group directory can also hold a __layout__.tsx (or .vue). The layout becomes a container for the group's subtree but, like the group itself, does not occupy a URL segment — React emits a pathless layout route (a route object without a path key) and Vue emits path: ''. To give it a URL segment, set an explicit path in its config (the escape hatch). See Directory Layouts __layout__.

Directory Layouts __layout__

A file named __layout__.tsx (React) or __layout__.vue (Vue) placed inside a directory turns that directory into a layout container — a shell component that wraps the directory's entire subtree. Create one by adding the layout file; a matching config file is optional:

Framework Layout file Config file
React __layout__.tsx __layout__.config.ts
Vue __layout__.vue __layout__.meta.ts

The config is matched by configPattern — the default is /**/*.config.{js,ts,jsx,tsx}. For Vue, the example below overrides it to /**/*.meta.ts.

⚠️ Extension caveat

The React layout config must use a glob-matched extension — .config.ts or .config.js, not .config.tsx. A __layout__.config.tsx will not be picked up.

A __layout__ file does not generate its own route. Instead it attaches a layout component plus layout config to the directory's container node.

Preserves the index layer (vs type: 'wrap')

__layout__ keeps the shell and the page as two separate files:

  • The shell component (__layout__.tsx) and the subtree-level config (__layout__.config.ts) land on the container node.
  • The page's own component (index.tsx) and its own index.config.ts land on the index child route.

type: 'wrap' instead hoists the page's extra props onto the parent and swallows the index layer, so shell and page cannot be separated.

URL segment

  • A normal directory inherits its URL segment: user/__layout__.tsx → container path: 'user'.
  • A group directory (e.g. (marketing)/__layout__.tsx) has no directory-derived segment, so React emits a pathless layout route (no path key) and Vue emits path: ''.
  • Escape hatch: inside a group directory, write an explicit path in the layout config to set the URL segment — e.g. (user)/__layout__.config.ts with path: '/user' behaves just like user/__layout__.

Nested groups produce nested containers. The path field (including the [currentPath] placeholder) works on the container node exactly like wrap does today.

Directory structure

src/pages/
├── __root__.tsx
├── (marketing)/
│   ├── __layout__.tsx
│   ├── pricing.tsx               # → /pricing
│   └── (legal)/
│       ├── __layout__.tsx
│       └── terms.tsx             # → /terms
├── user/
│   ├── __layout__.tsx            # → /user
│   ├── __layout__.config.ts      # subtree-level config (loader / handle / path)
│   ├── index.tsx                 # → /user (optional)
│   └── settings.tsx              # → /user/settings
└── 404.tsx

Generated tree — React

// segment directory: user/__layout__.tsx
{ path: 'user', element: <UserLayout />, /* layout config */,
  children: [ { index: true, element: <UserIndex />, /* page config */ }, { path: 'settings' } ] }
// group directory: (marketing)/__layout__.tsx → pathless
{ element: <MarketingLayout />,
  children: [ { path: 'pricing' }, { element: <LegalLayout />, children: [{ path: 'terms' }] } ] }

Generated tree — Vue

// segment directory
{ path: 'user', component: UserLayout, children: [ { path: '', component: UserIndex }, { path: 'settings' } ] }
// group directory → empty path
{ path: '', component: MarketingLayout, children: [ { path: 'pricing' } ] }

Note for Vue: vue-router has no index concept; an exact match on /user is expressed by a default child route with an empty path.

Root __layout__

A __layout__ in the pages root becomes the root container and replaces __root__ (top-level 404/notfound nests inside it, exactly as with __root__). Root __layout__ and __root__ cannot coexist — the plugin throws an error. With keepRoot: true, __root__ is kept as a plain route, so there is no conflict.

⚠️ Errors

The plugin throws for:

  • Multiple layout files resolving to the same directory.
  • A type field inside a layout config file — a layout is always a shell; remove the field.
  • [currentPath] inside a layout config in a route group — a group directory has no derived path; use a literal path instead.

Migration from type: 'wrap'

Aspect type: 'wrap' __layout__
URL segment Inherits the page's directory (hoisted) Inherits the directory; group → pathless
Shell vs page One file — the page doubles as the shell Two files — __layout__ shell + page stay separate
Index layer Swallowed (index layer removed) Preserved (index child route kept)
Config files index.config.ts carries both __layout__.config.ts (subtree) + index.config.ts (page)
Status Deprecated — works, not recommended for new code Recommended

Migration recipe:

  1. Move the shell markup from index.tsx into __layout__.tsx.
  2. Move subtree-level fields (loader / handle / path, etc.) from index.config.ts into __layout__.config.ts and drop type: 'wrap'.
  3. Add a new index.tsx if you want an exact page at the directory path.
  4. If the directory only has children (no page of its own), no new index.tsx is needed.

React Project

Install Dependencies
npm install react-router
Configure Vite
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import autoRouter from '@onoxm/vite-plugin-auto-router/react'

export default defineConfig({
  plugins: [react(), autoRouter()]
})
Directory Structure
src/
├── pages/
│   ├── index.tsx
│   ├── __root__.tsx
│   ├── 404.tsx
│   └── user/
│       ├── index.tsx
│       ├── index.config.ts
│       ├── [id].tsx
│       └── [id].config.ts
Special Pages
  • home page: Path automatically converted to /, used as home route
  • __root__ page: Used as root route container, wrapping all other routes. Deprecated — superseded by a root-level __layout__ file (pages/__layout__.tsx / pages/__layout__.vue), which becomes the root container. __root__ and keepRoot are planned for removal after 0.12.0.
  • 404 or notfound page: Path automatically converted to /*, used as the 404 route.
    • When __root__ is the active root container (the default), the 404 route is nested as the last child of the root route, so the 404 page reuses the root layout.
    • Otherwise (no __root__ page, or keepRoot: true), it is a standalone top-level route, not nested under any parent.
  • pages-root __layout__.tsx: A __layout__ at the pages root acts as the root container and replaces __root__; the two must not coexist (the plugin throws). See Directory Layouts __layout__
Page Configuration

Inherits from React Router RouteObject, with the following modifications:

  • Removed: Component, element, children, index
  • Added: type?: 'single' | 'wrap' (deprecated), asIndex?: boolean

⚠️ Important Note

Configuration files must not use named exports (export const, export function, etc.). Only export default is allowed. If a configuration file contains named exports, the plugin will skip the file and display a warning in the console.

type: 'single'

When type is set to single, the page component will be generated as an independent route:

// src/pages/user/index.config.ts
import { defineConfig } from 'virtual:onoxm-auto-router/react'

export default defineConfig({
  type: 'single'
})

Generated route structure:

// virtual:onoxm-auto-router/react
import type { RouteObject } from 'react-router'
import Pages404 from '../pages/404.tsx'
import Pages from '../pages/index.tsx'
import PagesRoot from '../pages/__root__.tsx'
import PagesUser from '../pages/user/index.tsx'
import PagesUserId from '../pages/user/[id]/index.tsx'

type PageConfig = Partial<
  Omit<
    RouteObject,
    'Component' | 'element' | 'children' | 'lazy' | 'index'
  > & {
    type?: 'single' | 'wrap'
    asIndex?: boolean
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteObject[] = [
  {
    path: '/',
    element: <PagesRoot />,
    children: [
      {
        path: '/',
        element: <Pages />
      },
      {
        path: '/user',
        children: [
          {
            index: true,
            element: <PagesUser />
          },
          {
            path: ':id',
            action: async () => {},
            loader: async ({ params }) => await { params },
            element: <PagesUserId />
          }
        ]
      },
      {
        path: '/*',
        element: <Pages404 />
      }
    ]
  }
]
type: 'wrap'

⚠️ Deprecated for new code. type: 'wrap' still works unchanged in 0.11.x–0.12.x (no warning, no behavior change), but prefer directory layouts __layout__ for new code — they preserve the index layer. Removal is planned after 0.12.0.

When type is set to wrap, the page component will act as a parent route container wrapping its child routes:

// src/pages/user/index.config.ts
import { defineConfig } from 'virtual:onoxm-auto-router/react'

export default defineConfig({
  type: 'wrap'
})

Generated route structure:

// virtual:onoxm-auto-router/react
import type { RouteObject } from 'react-router'
import Pages404 from '../pages/404.tsx'
import Pages from '../pages/index.tsx'
import PagesRoot from '../pages/__root__.tsx'
import PagesUser from '../pages/user/index.tsx'
import PagesUserId from '../pages/user/[id]/index.tsx'

type PageConfig = Partial<
  Omit<
    RouteObject,
    'Component' | 'element' | 'children' | 'lazy' | 'index'
  > & {
    type?: 'single' | 'wrap'
    asIndex?: boolean
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteObject[] = [
  {
    path: '/',
    element: <PagesRoot />,
    children: [
      {
        path: '/',
        element: <Pages />
      },
      {
        path: '/user',
        element: <PagesUser />,
        children: [
          {
            path: ':id',
            action: async () => {},
            loader: async ({ params }) => await { params },
            element: <PagesUserId />
          }
        ]
      },
      {
        path: '/*',
        element: <Pages404 />
      }
    ]
  }
]
hydrateFallbackElement / HydrateFallback (React Router lazy mode)

When the page configuration contains hydrateFallbackElement or HydrateFallback, the plugin automatically switches the page to React Router's lazy mode:

  • No longer generates element field or synchronous import statements
  • Instead generates a lazy property that dynamically imports the component module and returns Component
  • Preserves the original hydrate field for fallback rendering during SSR hydration

In React Router v7, these are two parallel fields — you can use either or both:

Field Type Accepts
hydrateFallbackElement React.ReactNode JSX element <Loading />
HydrateFallback React.ComponentType Component reference Loading

Usage 1: hydrateFallbackElement (JSX element)

The config file extension must be .config.tsx or .config.jsx:

// src/pages/index.config.tsx
import Loading from '../components/Loading'

export default defineConfig({
  hydrateFallbackElement: <Loading />
})

Usage 2: HydrateFallback (component reference)

// src/pages/index.config.ts (component reference works with .ts/.js)
import Loading from '../components/Loading'

export default defineConfig({
  HydrateFallback: Loading
})

Generated route structure (both usages produce the same output, differing only in the hydrate field):

// virtual:onoxm-auto-router/react
import type { RouteObject } from 'react-router'
import Loading from './components/Loading.tsx'

type PageConfig = Partial<
  Omit<
    RouteObject,
    'Component' | 'element' | 'children' | 'lazy' | 'index'
  > & {
    type?: 'single' | 'wrap'
    asIndex?: boolean
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteObject[] = [
  {
    path: '/',
    hydrateFallbackElement: <Loading />,  // or HydrateFallback: Loading
    lazy: () => import('./pages/index.tsx').then(m => ({ Component: m.default }))
  }
]

lazy Mode Priority

Page Config Plugin lazy Option Generation Mode
Contains hydrateFallbackElement or HydrateFallback Any value React Router lazy (highest priority, page-level overrides global)
No hydrate config true React.lazy() + <Suspense>
No hydrate config false Synchronous import + element

Mixed Scenario

In the same project, routes with hydrate config use React Router lazy, while routes without it follow the global lazy option (React.lazy or synchronous import). The plugin intelligently determines whether to import { lazy, Suspense } from 'react' — when all routes use React Router lazy, it is not imported.

Note

  • When using hydrateFallbackElement with JSX elements, the extension must be .config.tsx or .config.jsx; when using HydrateFallback with component references, the extension can be .config.ts or .config.js
  • Imports of fallback components (e.g., Loading) are automatically collected, deduplicated, and injected into the generated route code
  • Do not use lowercase hydrateFallback (this field name does not exist in React Router v7; only HydrateFallback with capital H and hydrateFallbackElement are recognized)

Vue Project

Install Dependencies
npm install vue-router
Configure Vite
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import autoRouter from '@onoxm/vite-plugin-auto-router/vue'

export default defineConfig({
  plugins: [
    vue(),
    autoRouter({
      pagesDir: './src/views',
      configPattern: '/**/*.meta.ts'
    })
  ]
})
Directory Structure
src/
├── views/
│   ├── 404.vue
│   ├── home/
│   │   ├── index.vue
│   │   └── index.meta.ts
│   └── user/
│       ├── index.vue
│       ├── index.meta.ts
│       ├── [id].vue
│       └── [id].meta.ts
Special Pages
  • home page: Path automatically converted to /, used as home route
  • __root__ page: Used as root route container, wrapping all other routes. Deprecated — superseded by a root-level __layout__ file (pages/__layout__.tsx / pages/__layout__.vue), which becomes the root container. __root__ and keepRoot are planned for removal after 0.12.0.
  • 404 or notfound page: Path automatically converted to /:pathMatch(.*)*, used as the 404 route.
    • When __root__ is the active root container (the default), the 404 route is nested as the last child of the root route, so the 404 page reuses the root layout.
    • Otherwise (no __root__ page, or keepRoot: true), it is a standalone top-level route, not nested under any parent.
  • views-root __layout__.vue: A __layout__ at the views root acts as the root container and replaces __root__; the two must not coexist (the plugin throws). See Directory Layouts __layout__
Page Configuration

Inherits from Vue Router RouteRecordRaw, with the following modifications:

  • Removed: component, children
  • Added: type?: 'single' | 'wrap' (deprecated), asIndex?: boolean

⚠️ Important Note

Configuration files must not use named exports (export const, export function, etc.). Only export default is allowed. If a configuration file contains named exports, the plugin will skip the file and display a warning in the console.

type: 'single'

When type is set to single, the page component will be generated as an independent route:

// src/views/user/index.meta.ts
import { defineConfig } from 'virtual:onoxm-auto-router/vue'

export default defineConfig({
  type: 'single'
})

Generated route structure:

// virtual:onoxm-auto-router/vue
import type { RouteRecordRaw } from 'vue-router'
import Views404 from '../views/404.vue'
import ViewsHome from '../views/home/index.vue'
import ViewsUser from '../views/user/index.vue'
import ViewsUserId from '../views/user/[id]/index.vue'

type PageConfig = Partial<
  Omit<RouteRecordRaw, 'component' | 'children'> & {
    type?: 'single' | 'wrap'
    asIndex?: boolean
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home',
    component: ViewsHome
  },
  {
    path: '/user',
    children: [
      {
        path: '',
        component: ViewsUser
      },
      {
        path: ':id',
        component: ViewsUserId
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    component: Views404
  }
]
type: 'wrap'

⚠️ Deprecated for new code. type: 'wrap' still works unchanged in 0.11.x–0.12.x (no warning, no behavior change), but prefer directory layouts __layout__ for new code — they preserve the index layer. Removal is planned after 0.12.0.

When type is set to wrap, the page component will act as a parent route container wrapping its child routes:

// src/views/user/index.meta.ts
import { defineConfig } from 'virtual:onoxm-auto-router/vue'

export default defineConfig({
  type: 'wrap'
})

Generated route structure:

// virtual:onoxm-auto-router/vue
import type { RouteRecordRaw } from 'vue-router'
import Views404 from '../views/404.vue'
import ViewsHome from '../views/home/index.vue'
import ViewsUser from '../views/user/index.vue'
import ViewsUserId from '../views/user/[id]/index.vue'

type PageConfig = Partial<
  Omit<RouteRecordRaw, 'component' | 'children'> & {
    type?: 'single' | 'wrap'
    asIndex?: boolean
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home',
    component: ViewsHome
  },
  {
    path: '/user',
    component: ViewsUser,
    children: [
      {
        path: ':id',
        component: ViewsUserId
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    component: Views404
  }
]

⚙️ Configuration Options

Plugin Configuration

Option Type Default Description
framework 'react' | 'vue' 'react' Framework type (usually determined by the import path; no need to set manually)
pagesDir string './src/pages' Pages directory
keepHome boolean false Whether to keep home page path
keepRoot boolean false Whether to keep __root__ page path. Deprecated — keepRoot only exists to control the deprecated __root__ behavior and will be removed with it
lazy boolean true Whether to enable lazy loading
configPattern string '/**/*.config.{js,ts,jsx,tsx}' Config file format
log false | 'tree' | 'json' false Output route preview to console: 'tree' for readable tree structure, 'json' for formatted (pretty-printed) JSON
injectTypes boolean true Inject the virtual module type reference into vite-env.d.ts automatically; set to false to never touch your source files
log Usage Example

The log option only controls whether the route tree preview is printed to the console. Supports three values:

  • false (default): no output
  • 'tree': outputs a readable route tree structure, component list, config imports, and target file path
  • 'json': outputs formatted (2-space indented) JSON (route tree with resolved component names, config imports, framework, routes file path) — readable in the console, still parseable by tooling
autoRouter({
  log: 'tree' // Print a readable route tree to the console
})

// or
autoRouter({
  log: 'json' // Print route info as JSON (pipe to a file or another tool)
})

Virtual Module

Route code is always served in-memory via the Vite virtual module virtual:onoxm-auto-router/react (or /vue). It is never written to disk — no generated artifacts are left in your project.

🚨 First-time Setup Required

The plugin auto-injects /// <reference types="@onoxm/vite-plugin-auto-router/virtual" /> into vite-env.d.ts during Vite startup. If your build script runs tsc before vite build (e.g., tsc -b && vite build), the first build may fail because tsc cannot resolve virtual:onoxm-auto-router/* yet.

Fix: Run vite dev or vite build once to let the plugin create vite-env.d.ts, then commit that file to your version control. All subsequent builds will work.

Import directly from the virtual module in your app code:

// src/router/index.ts
import { createBrowserRouter } from 'react-router'
import { routes } from 'virtual:onoxm-auto-router/react'

export const router = createBrowserRouter(routes)

Page config files also import defineConfig from the virtual module:

// src/pages/user/index.config.ts
import { defineConfig } from 'virtual:onoxm-auto-router/react'

export default defineConfig({
  type: 'single'
})

Build & SSR compatibility

The virtual module works in both vite dev and vite build, including SSR builds. The virtual module is resolved during the build phase just like regular source files. The generated route code is pure data (no browser API dependencies), so it is compatible with server-side rendering environments. However, the plugin does not provide SSR-specific entry points — for React SSR, users need to use createStaticRouter instead of createBrowserRouter in their server entry.

TypeScript type declaration auto-injection

The virtual module virtual:onoxm-auto-router/{react|vue} is an in-memory module with no physical file on disk, so TypeScript cannot resolve its named exports (routes, defineConfig) by default. To pass TS type checks, the module's type declarations must be added to the project's vite-env.d.ts.

The plugin package exposes the type declarations via the ./virtual subpath (@onoxm/vite-plugin-auto-router/virtual). When the project is a TypeScript project (i.e., tsconfig.json exists), the plugin silently auto-injects the following into vite-env.d.ts during Vite's configResolved hook (triggered by both dev and build):

/// <reference types="@onoxm/vite-plugin-auto-router/virtual" />

Injection rules:

Scenario Behavior
src/vite-env.d.ts exists and already contains the reference Skipped, content unchanged (idempotent)
src/vite-env.d.ts exists but lacks the reference Appended after a blank line
Root vite-env.d.ts exists but no src/ one Appended to the root file
Both src/vite-env.d.ts and root exist Processes the src/ one, leaves root untouched
Neither exists Creates src/vite-env.d.ts with the reference
Any error Only warns, does not throw, does not block Vite startup

Injection rewrites a file that is usually tracked in version control. Set injectTypes: false if you would rather keep the plugin away from your source tree — you then declare the virtual module types yourself.

Page Configuration

Option Type Default Description
type 'single' | 'wrap' 'single' Route type (deprecated — prefer __layout__)
path string undefined Route path, supports [currentPath] placeholder to reference current path
asIndex boolean undefined Push this leaf page down into an index child route under its own path (see below)
* any any Inherits from router config
path Usage Example

Use the [currentPath] placeholder to reference the current path when replacing:

// src/pages/user/index.config.ts
import { defineConfig } from 'virtual:onoxm-auto-router/react'

export default defineConfig({
  // Replace /user with /users/v2
  path: '/users/v2'
})

// Or use the placeholder to add a suffix to the current path
export default defineConfig({
  // Replace /user with /user/v2
  path: '[currentPath]/v2'
})
asIndex Usage Example

By default a leaf page emits { path, element } directly, with no extra index child route. To make a page "own its path while its content sits at the index level", set asIndex: true in its config:

// src/pages/about.config.ts
import { defineConfig } from 'virtual:onoxm-auto-router/react'

export default defineConfig({
  asIndex: true
})

The generated route uses each framework's native shape for an index child route:

// React
{ path: '/about', children: [{ index: true, element: About }] }

// Vue
{ path: '/about', children: [{ path: '', component: About }] }

Notes:

  • Only meaningful for leaf pages: a page that already has child routes is split regardless, and asIndex does not change the result; false is the same as leaving it unset.
  • Misuse throws: setting it in a __layout__.config.* file, or alongside type: 'wrap', raises an error — a layout or wrap page is already a shell, so "push myself down into an index child route" does not apply.
  • You cannot write index directly in a React page config (index is react-router's output field, not a config entry, and has been excluded from the writable PageConfig fields); use asIndex for the same intent.

📄 License

MIT

Related Packages

@onoxm/auto-router-preset-vue

a vite plugin for automatically generating react or vue route files

145
vite-plugin-remix-router

Remix style file-system routing for React and Vite

4,716 25
@tomjs/vite-plugin-iconify

A Vite plugin for handling iconify icon sets in environments without internet ac...

11,231 9
@tachibana-shin/vite-plugin-pages

File system base vue-router plugin for Vite

2,482 2,076
vite-plugin-pages2

File system base vue-router plugin for Vite

2,171 1

Version History

Version Released License
0.11.0 MIT
0.10.7 MIT
0.10.6 MIT
0.10.5 MIT
0.10.4 MIT