@tanstack/router-plugin
Version:
Modern and scalable routing for React applications
251 lines (191 loc) • 9.03 kB
Markdown
---
name: router-plugin
description: >-
TanStack Router bundler plugin for route generation and automatic
code splitting. Supports Vite, Webpack, Rspack, and esbuild.
Configures autoCodeSplitting, routesDirectory, target framework,
and code split groupings.
metadata:
type: core
library: tanstack-router
library_version: '1.168.23'
sources:
- TanStack/router:packages/router-plugin/src
- TanStack/router:docs/router/routing/file-based-routing.md
- TanStack/router:docs/router/guide/code-splitting.md
---
# Router Plugin (`/router-plugin`)
Bundler plugin that powers TanStack Router's file-based routing and automatic code splitting. Works with Vite, Webpack, Rspack, and esbuild via unplugin.
> **CRITICAL**: The router plugin MUST come before the framework plugin (React, Solid, Vue) in the Vite config. Wrong order causes route generation and code splitting to fail silently.
## Install
```bash
npm install -D /router-plugin
```
## Bundler Setup
### Vite (most common)
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
// MUST come before react()
tanstackRouter({
target: 'react',
autoCodeSplitting: true,
}),
react(),
],
})
```
### Webpack
```ts
// webpack.config.js
const { tanstackRouter } = require('/router-plugin/webpack')
module.exports = {
plugins: [
tanstackRouter({
target: 'react',
autoCodeSplitting: true,
}),
],
}
```
### Rspack
```ts
// rspack.config.js
const { tanstackRouter } = require('/router-plugin/rspack')
module.exports = {
plugins: [
tanstackRouter({
target: 'react',
autoCodeSplitting: true,
}),
],
}
```
### esbuild
```ts
import { tanstackRouter } from '@tanstack/router-plugin/esbuild'
import esbuild from 'esbuild'
esbuild.build({
plugins: [
tanstackRouter({
target: 'react',
autoCodeSplitting: true,
}),
],
})
```
## Configuration Options
### Core Options
| Option | Type | Default | Description |
| ----------------------- | ----------------------------- | -------------------------- | ------------------------------------------ |
| `target` | `'react' \| 'solid' \| 'vue'` | `'react'` | Target framework |
| `routesDirectory` | `string` | `'./src/routes'` | Directory containing route files |
| `generatedRouteTree` | `string` | `'./src/routeTree.gen.ts'` | Path for generated route tree |
| `autoCodeSplitting` | `boolean` | `undefined` | Enable automatic code splitting |
| `enableRouteGeneration` | `boolean` | `true` | Set to `false` to disable route generation |
### File Convention Options
| Option | Type | Default | Description |
| ------------------------ | ------------------------------------------------------- | ----------- | ------------------------------------ |
| `routeFilePrefix` | `string` | `undefined` | Prefix filter for route files |
| `routeFileIgnorePrefix` | `string` | `'-'` | Prefix to exclude files from routing |
| `routeFileIgnorePattern` | `string` | `undefined` | Pattern to exclude from routing |
| `indexToken` | `string \| RegExp \| { regex: string; flags?: string }` | `'index'` | Token identifying index routes |
| `routeToken` | `string \| RegExp \| { regex: string; flags?: string }` | `'route'` | Token identifying route config files |
### Code Splitting Options
```ts
tanstackRouter({
target: 'react',
autoCodeSplitting: true,
codeSplittingOptions: {
// Default groupings for all routes
defaultBehavior: [['component'], ['errorComponent'], ['notFoundComponent']],
// Per-route custom splitting
splitBehavior: ({ routeId }) => {
if (routeId === '/dashboard') {
// Keep loader and component together for dashboard
return [['loader', 'component'], ['errorComponent']]
}
// Return undefined to use defaultBehavior
},
},
})
```
### Output Options
| Option | Type | Default | Description |
| --------------------------- | ---------------------- | ---------- | -------------------------------- |
| `quoteStyle` | `'single' \| 'double'` | `'single'` | Quote style in generated code |
| `semicolons` | `boolean` | `false` | Use semicolons in generated code |
| `disableTypes` | `boolean` | `false` | Disable TypeScript types |
| `disableLogging` | `boolean` | `false` | Suppress plugin logs |
| `addExtensions` | `boolean \| string` | `false` | Add file extensions to imports |
| `enableRouteTreeFormatting` | `boolean` | `true` | Format generated route tree |
### Virtual Route Config
```ts
import { routes } from './routes'
tanstackRouter({
target: 'react',
virtualRouteConfig: routes, // or './routes.ts'
})
```
## How It Works
The composed plugin assembles up to 3 sub-plugins:
1. **Route Generator** (always) — Watches route files and generates `routeTree.gen.ts`
2. **Code Splitter** (when `autoCodeSplitting: true`) — Splits route files into lazy-loaded chunks using virtual modules
3. **HMR** (dev mode, when code splitter is off) — Hot-reloads route changes without full refresh
## Route Refactor Workflow
When moving, renaming, adding, or deleting file routes:
1. Change the source files under `routesDirectory`. Keep the exported route identifier named `Route`.
2. Let the bundler plugin regenerate the tree, or run `pnpm exec tsr generate` when the project uses the CLI.
3. Inspect the generated diff for the expected route IDs, parents, paths, and imports. Never repair `routeTree.gen.ts` by hand.
4. Update links, redirects, `from` narrowing, params, preload calls, and tests that reference the old route.
5. Run route-generation tests, type tests, and a production build. A passing editor typecheck does not prove the plugin generated or split the new route correctly.
Commit `routeTree.gen.ts`; it is generated source used by the application at runtime.
## Individual Plugin Exports
For advanced use, each sub-plugin is exported separately from the Vite entry:
```ts
import {
tanstackRouter, // Composed (default)
tanstackRouterGenerator, // Generator only
tanStackRouterCodeSplitter, // Code splitter only
} from '/router-plugin/vite'
```
## Common Mistakes
### 1. CRITICAL: Wrong plugin order in Vite config
The router plugin must come before the framework plugin. Otherwise, route generation and code splitting fail silently.
```ts
// WRONG — react() before tanstackRouter()
plugins: [react(), tanstackRouter({ target: 'react' })]
// CORRECT — tanstackRouter() first
plugins: [tanstackRouter({ target: 'react' }), react()]
```
### 2. HIGH: Missing target option for non-React frameworks
The `target` defaults to `'react'`. For Solid or Vue, you must set it explicitly.
```ts
// WRONG for Solid — generates React imports
tanstackRouter({ autoCodeSplitting: true })
// CORRECT for Solid
tanstackRouter({ target: 'solid', autoCodeSplitting: true })
```
### 3. MEDIUM: Confusing autoCodeSplitting with manual lazy routes
When `autoCodeSplitting` is enabled, the plugin handles splitting automatically. You do NOT need manual `createLazyRoute` or `lazyRouteComponent` calls — the plugin transforms your route files at build time.
```tsx
// WRONG — manual lazy loading with autoCodeSplitting enabled
const LazyAbout = lazyRouteComponent(() => import('./about'))
// CORRECT — just write normal route files, plugin handles splitting
// src/routes/about.tsx
export const Route = createFileRoute('/about')({
component: AboutPage,
})
function AboutPage() {
return <h1>About</h1>
}
```
### 4. HIGH: Editing the generated route tree
Changes to `routeTree.gen.ts` are overwritten and can leave source routes, generated types, and runtime routing out of sync. Fix route filenames or plugin configuration, regenerate, and verify the generated diff instead.
## Cross-References
- [router-core/code-splitting](../../../router-core/skills/router-core/code-splitting/SKILL.md) — manual code splitting concepts
- [virtual-file-routes](../../../virtual-file-routes/skills/virtual-file-routes/SKILL.md) — programmatic route trees