134 lines
3.9 KiB
Markdown
134 lines
3.9 KiB
Markdown
# org-virtual-routes: Understand Virtual File Routes
|
|
|
|
## Priority: LOW
|
|
|
|
## Explanation
|
|
|
|
Virtual routes are automatically generated placeholder routes in the route tree when you have a `.lazy.tsx` file without a corresponding main route file. They provide the minimal configuration needed to anchor lazy-loaded components.
|
|
|
|
## Bad Example
|
|
|
|
```tsx
|
|
// Creating unnecessary boilerplate main route files
|
|
// routes/settings.tsx - Just to have a file
|
|
export const Route = createFileRoute('/settings')({
|
|
// Empty - no loader, no beforeLoad, nothing
|
|
})
|
|
|
|
// routes/settings.lazy.tsx - Actual component
|
|
export const Route = createLazyFileRoute('/settings')({
|
|
component: SettingsPage,
|
|
})
|
|
|
|
// The main file is unnecessary boilerplate
|
|
```
|
|
|
|
## Good Example: Let Virtual Routes Handle It
|
|
|
|
```tsx
|
|
// Delete routes/settings.tsx entirely!
|
|
|
|
// routes/settings.lazy.tsx - Only file needed
|
|
export const Route = createLazyFileRoute('/settings')({
|
|
component: SettingsPage,
|
|
pendingComponent: SettingsLoading,
|
|
errorComponent: SettingsError,
|
|
})
|
|
|
|
function SettingsPage() {
|
|
return <div>Settings Content</div>
|
|
}
|
|
|
|
// TanStack Router auto-generates a virtual route:
|
|
// {
|
|
// path: '/settings',
|
|
// // Minimal config to anchor the lazy file
|
|
// }
|
|
```
|
|
|
|
## Good Example: When You DO Need Main Route File
|
|
|
|
```tsx
|
|
// routes/dashboard.tsx - Need this for loader/beforeLoad
|
|
export const Route = createFileRoute('/dashboard')({
|
|
beforeLoad: async ({ context }) => {
|
|
if (!context.auth.isAuthenticated) {
|
|
throw redirect({ to: '/login' })
|
|
}
|
|
},
|
|
loader: async ({ context: { queryClient } }) => {
|
|
await queryClient.ensureQueryData(dashboardQueries.stats())
|
|
},
|
|
// Component is in lazy file
|
|
})
|
|
|
|
// routes/dashboard.lazy.tsx
|
|
export const Route = createLazyFileRoute('/dashboard')({
|
|
component: DashboardPage,
|
|
pendingComponent: DashboardSkeleton,
|
|
})
|
|
|
|
// Main file IS needed here because we have loader/beforeLoad
|
|
```
|
|
|
|
## Decision Guide
|
|
|
|
| Route Has... | Need Main File? | Use Virtual? |
|
|
|--------------|-----------------|--------------|
|
|
| Only component | No | Yes |
|
|
| loader | Yes | No |
|
|
| beforeLoad | Yes | No |
|
|
| validateSearch | Yes | No |
|
|
| loaderDeps | Yes | No |
|
|
| Just pendingComponent/errorComponent | No | Yes |
|
|
|
|
## Good Example: File Structure with Virtual Routes
|
|
|
|
```
|
|
routes/
|
|
├── __root.tsx # Always needed
|
|
├── index.tsx # Has loader
|
|
├── about.lazy.tsx # Virtual route (no main file)
|
|
├── contact.lazy.tsx # Virtual route (no main file)
|
|
├── dashboard.tsx # Has beforeLoad (auth)
|
|
├── dashboard.lazy.tsx # Component
|
|
├── posts.tsx # Has loader
|
|
├── posts.lazy.tsx # Component
|
|
├── posts/
|
|
│ ├── $postId.tsx # Has loader
|
|
│ └── $postId.lazy.tsx # Component
|
|
└── settings/
|
|
├── index.lazy.tsx # Virtual route
|
|
├── profile.lazy.tsx # Virtual route
|
|
└── security.tsx # Has beforeLoad (requires re-auth)
|
|
```
|
|
|
|
## Good Example: Generated Route Tree
|
|
|
|
```tsx
|
|
// routeTree.gen.ts (auto-generated)
|
|
import { Route as rootRoute } from './routes/__root'
|
|
import { Route as aboutLazyRoute } from './routes/about.lazy' // Virtual parent
|
|
|
|
export const routeTree = rootRoute.addChildren([
|
|
// Virtual route created for about.lazy.tsx
|
|
createRoute({
|
|
path: '/about',
|
|
getParentRoute: () => rootRoute,
|
|
}).lazy(() => import('./routes/about.lazy').then(m => m.Route)),
|
|
|
|
// Regular route with explicit main file
|
|
dashboardRoute.addChildren([...]),
|
|
])
|
|
```
|
|
|
|
## Context
|
|
|
|
- Virtual routes reduce boilerplate for simple pages
|
|
- Only works with file-based routing
|
|
- Auto-generated in `routeTree.gen.ts`
|
|
- Main route file needed for any "critical path" config
|
|
- Critical: loader, beforeLoad, validateSearch, loaderDeps, context
|
|
- Non-critical (can be in lazy): component, pendingComponent, errorComponent
|
|
- Check generated route tree to verify virtual routes
|