# 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
