Split route components out of the main bundle
Load each route's components only when that route is visited.
Prefer the router plugin's automatic code splitting; use .lazy.tsx files when you need manual control.
Implementation
- Enable
autoCodeSplitting: truein the router bundler plugin, imported astanstackRouterfrom@tanstack/router-plugin/viteor the matching bundler entry. The olderTanStackRouterViteexport is deprecated. - Without automatic splitting, move
component,pendingComponent,errorComponent, andnotFoundComponentinto a.lazy.tsxfile created withcreateLazyFileRoute. - Keep options needed before rendering in the main route file:
loader,beforeLoad,validateSearch,loaderDeps, and context. - A route with only a
.lazy.tsxfile gets a generated virtual route, so an empty main route file is unnecessary. - In components split from the route file, use
getRouteApi('/path')for typed hooks instead of importing the route.
Rationale
Loaders and search validation must run before a route renders, so they stay in the main bundle. Components, and the heavy libraries they import, are needed only when their route renders, so splitting them keeps the initial download small. Automatic splitting does this for every route without a second file per route.
Examples
Incorrect (counterexample):
// routes/dashboard.tsx
import { HeavyChart } from 'heavy-chart-library';
export const Route = createFileRoute('/dashboard')({
loader: ({ context }) => context.queryClient.ensureQueryData(dashboardQueries.stats()),
component: () => <HeavyChart />,
});
Without code splitting, the chart library ships in the initial bundle for every visitor.
Correct:
// vite.config.ts
import { tanstackRouter } from '@tanstack/router-plugin/vite';
export default defineConfig({
plugins: [tanstackRouter({ target: 'react', autoCodeSplitting: true }), react()],
});
The same route file now loads its component, and the chart library, only when /dashboard renders.
Validation
Inspect the build output and check that each route's component is in its own chunk, and that heavy libraries are not in the entry chunk.
A small app whose whole bundle loads quickly does not need route splitting.