mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-05 12:17:36 +00:00
148 lines
3.9 KiB
Markdown
148 lines
3.9 KiB
Markdown
# split-lazy-routes: Use .lazy.tsx for Code Splitting
|
|||
|
|
|
||
|
|
## Priority: MEDIUM
|
||
|
|
|
||
|
|
## Explanation
|
||
|
|
|
||
|
|
Split route components into `.lazy.tsx` files to reduce initial bundle size. The main route file keeps critical configuration (path, loaders, search validation), while lazy files contain components that load on-demand.
|
||
|
|
|
||
|
|
## Bad Example
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// routes/dashboard.tsx - Everything in one file
|
||
|
|
import { createFileRoute } from '@tanstack/react-router'
|
||
|
|
import { HeavyChartLibrary } from 'heavy-chart-library'
|
||
|
|
import { ComplexDataGrid } from 'complex-data-grid'
|
||
|
|
import { AnalyticsWidgets } from './components/AnalyticsWidgets'
|
||
|
|
|
||
|
|
export const Route = createFileRoute('/dashboard')({
|
||
|
|
loader: async ({ context }) => {
|
||
|
|
return context.queryClient.ensureQueryData(dashboardQueries.stats())
|
||
|
|
},
|
||
|
|
component: DashboardPage, // Entire component in main bundle
|
||
|
|
})
|
||
|
|
|
||
|
|
function DashboardPage() {
|
||
|
|
// Heavy components loaded even if user never visits dashboard
|
||
|
|
return (
|
||
|
|
<div>
|
||
|
|
<HeavyChartLibrary data={useLoaderData()} />
|
||
|
|
<ComplexDataGrid />
|
||
|
|
<AnalyticsWidgets />
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Good Example
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// routes/dashboard.tsx - Only critical config
|
||
|
|
import { createFileRoute } from '@tanstack/react-router'
|
||
|
|
|
||
|
|
export const Route = createFileRoute('/dashboard')({
|
||
|
|
loader: async ({ context }) => {
|
||
|
|
return context.queryClient.ensureQueryData(dashboardQueries.stats())
|
||
|
|
},
|
||
|
|
// No component - it's in the lazy file
|
||
|
|
})
|
||
|
|
|
||
|
|
// routes/dashboard.lazy.tsx - Lazy-loaded component
|
||
|
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
||
|
|
import { HeavyChartLibrary } from 'heavy-chart-library'
|
||
|
|
import { ComplexDataGrid } from 'complex-data-grid'
|
||
|
|
import { AnalyticsWidgets } from './components/AnalyticsWidgets'
|
||
|
|
|
||
|
|
export const Route = createLazyFileRoute('/dashboard')({
|
||
|
|
component: DashboardPage,
|
||
|
|
pendingComponent: DashboardSkeleton,
|
||
|
|
errorComponent: DashboardError,
|
||
|
|
})
|
||
|
|
|
||
|
|
function DashboardPage() {
|
||
|
|
const data = Route.useLoaderData()
|
||
|
|
return (
|
||
|
|
<div>
|
||
|
|
<HeavyChartLibrary data={data} />
|
||
|
|
<ComplexDataGrid />
|
||
|
|
<AnalyticsWidgets />
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
function DashboardSkeleton() {
|
||
|
|
return <div className="dashboard-skeleton">Loading dashboard...</div>
|
||
|
|
}
|
||
|
|
|
||
|
|
function DashboardError({ error }: { error: Error }) {
|
||
|
|
return <div>Failed to load dashboard: {error.message}</div>
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## What Goes Where
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// Main route file (routes/example.tsx)
|
||
|
|
// - path configuration (implicit from file location)
|
||
|
|
// - validateSearch
|
||
|
|
// - beforeLoad (auth checks, redirects)
|
||
|
|
// - loader (data fetching)
|
||
|
|
// - loaderDeps
|
||
|
|
// - context manipulation
|
||
|
|
// - Static route data
|
||
|
|
|
||
|
|
// Lazy file (routes/example.lazy.tsx)
|
||
|
|
// - component
|
||
|
|
// - pendingComponent
|
||
|
|
// - errorComponent
|
||
|
|
// - notFoundComponent
|
||
|
|
```
|
||
|
|
|
||
|
|
## Using getRouteApi in Lazy Components
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// routes/posts/$postId.lazy.tsx
|
||
|
|
import { createLazyFileRoute, getRouteApi } from '@tanstack/react-router'
|
||
|
|
|
||
|
|
const route = getRouteApi('/posts/$postId')
|
||
|
|
|
||
|
|
export const Route = createLazyFileRoute('/posts/$postId')({
|
||
|
|
component: PostPage,
|
||
|
|
})
|
||
|
|
|
||
|
|
function PostPage() {
|
||
|
|
// Type-safe access without importing main route file
|
||
|
|
const { postId } = route.useParams()
|
||
|
|
const data = route.useLoaderData()
|
||
|
|
|
||
|
|
return <article>{/* ... */}</article>
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Automatic Code Splitting
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// vite.config.ts - Enable automatic splitting
|
||
|
|
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
|
||
|
|
|
||
|
|
export default defineConfig({
|
||
|
|
plugins: [
|
||
|
|
TanStackRouterVite({
|
||
|
|
autoCodeSplitting: true, // Automatically splits all route components
|
||
|
|
}),
|
||
|
|
react(),
|
||
|
|
],
|
||
|
|
})
|
||
|
|
|
||
|
|
// With autoCodeSplitting, you don't need .lazy.tsx files
|
||
|
|
// The plugin handles the splitting automatically
|
||
|
|
```
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
- Lazy loading reduces initial bundle size significantly
|
||
|
|
- Loaders are NOT lazy - they need to run before rendering
|
||
|
|
- `createLazyFileRoute` only accepts component-related options
|
||
|
|
- Use `getRouteApi()` for type-safe hook access in lazy files
|
||
|
|
- Consider `autoCodeSplitting: true` for simpler setup
|
||
|
|
- Virtual routes auto-generate when only .lazy.tsx exists
|