
code-splitting
Automatic code splitting (autoCodeSplitting), .lazy.tsx convention, createLazyFileRoute, createLazyRoute, lazyRouteComponent, getRouteApi for typed hooks in split files, codeSplitGroupings per-route override, splitBehavior programmatic config, critical vs non-critical properties.
- Installs
- 199
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/tanstack/router --skill code-splitting- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads code-splitting automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Code Splitting
TanStack Router separates route code into critical (required to match and start loading) and non-critical (can be lazy-loaded). The bundler plugin can split automatically, or you can split manually with .lazy.tsx files.
CRITICAL: Never
exportcomponent functions from route files — exported functions are included in the main bundle and bypass code splitting entirely.
CRITICAL: Use
getRouteApi('/path')in code-split files, NOTimport { Route } from './route'. Importing Route defeats code splitting.
What Stays in the Main Bundle (Critical)
- Path parsing/serialization
validateSearchloader,beforeLoad- Route context, static data
- Links, scripts, styles
What Gets Split (Non-Critical)
componenterrorComponentpendingComponentnotFoundComponent
The
loaderis NOT split by default. It is already async, so splitting it adds a double async cost: fetch the chunk, then execute the loader. Only split the loader if you have a specific reason.
Setup: Automatic Code Splitting
Enable autoCodeSplitting: true in the bundler plugin. This is the recommended approach.
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
// TanStack Router plugin MUST come before the framework plugin
tanstackRouter({
autoCodeSplitting: true,
}),
react(),
],
})
With this enabled, route files are automatically transformed. Components are split into separate chunks; loaders stay in the main bundle. No .lazy.tsx files needed.
// src/routes/posts.tsx — everything in one file, splitting is automatic
import { createFileRoute } from '@tanstack/react-router'
import { fetchPosts } from '../api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: PostsComponent,
})
// NOT exported — this is critical for automatic code splitting to work
function PostsComponent() {
const posts = Route.useLoaderData()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
Manual Splitting with .lazy.tsx
If you cannot use automatic code splitting (e.g. CLI-only, no bundler plugin), split manually into two files:
// src/routes/posts.tsx — critical route config only
import { createFileRoute } from '@tanstack/react-router'
import { fetchPosts } from '../api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})
// src/routes/posts.lazy.tsx — non-critical (lazy-loaded)
import { createLazyFileRoute } from '@tanstack/react-router'
export const Route = createLazyFileRoute('/posts')({
component: PostsComponent,
})
function PostsComponent() {
// Use getRouteApi to access typed hooks without importing Route
return <div>Posts</div>
}
createLazyFileRoute supports only: component, errorComponent, pendingComponent, notFoundComponent.
Virtual Routes
If splitting leaves the critical route file empty, delete it entirely. A virtual route is auto-generated in routeTree.gen.ts:
// src/routes/about.lazy.tsx — no about.tsx needed
import { createLazyFileRoute } from '@tanstack/react-router'
export const Route = createLazyFileRoute('/about')({
component: () => <h1>About Us</h1>,
})
Code-Based Splitting
For code-based (non-file-based) routing, use createLazyRoute and the .lazy() method:
// src/posts.lazy.tsx
import { createLazyRoute } from '@tanstack/react-router'
export const Route = createLazyRoute('/posts')({
component: PostsComponent,
})
function PostsComponent() {
return <div>Posts</div>
}
// src/app.tsx
import { createRoute } from '@tanstack/react-router'
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
}).lazy(() => import('./posts.lazy').then((d) => d.Route))
Accessing Typed Hooks in Split Files: getRouteApi
When your component lives in a separate file, use getRouteApi to get typed access to route hooks without importing the Route object:
// src/routes/posts.lazy.tsx
import { createLazyFileRoute, getRouteApi } from '@tanstack/react-router'
const routeApi = getRouteApi('/posts')
export const Route = createLazyFileRoute('/posts')({
component: PostsComponent,
})
function PostsComponent() {
const posts = routeApi.useLoaderData()
const { page } = routeApi.useSearch()
const params = routeApi.useParams()
const context = routeApi.useRouteContext()
return <div>Posts page {page}</div>
}
getRouteApi provides: useLoaderData, useLoaderDeps, useMatch, useParams, useRouteContext, useSearch.
Per-Route Split Overrides: codeSplitGroupings
Override split behavior for a specific route by adding codeSplitGroupings directly in the route file:
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'
import { loadPostsData } from './-heavy-posts-utils'
export const Route = createFileRoute('/posts')({
// Bundle loader and component together for this route
codeSplitGroupings: [['loader', 'component']],
loader: () => loadPostsData(),
component: PostsComponent,
})
function PostsComponent() {
const data = Route.useLoaderData()
return <div>{data.title}</div>
}
Global Split Configuration
defaultBehavior — Change Default Groupings
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
autoCodeSplitting: true,
codeSplittingOptions: {
defaultBehavior: [
// Bundle all UI components into one chunk
[
'component',
'pendingComponent',
'errorComponent',
'notFoundComponent',
],
],
},
}),
],
})
splitBehavior — Programmatic Per-Route Logic
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
autoCodeSplitting: true,
codeSplittingOptions: {
splitBehavior: ({ routeId }) => {
if (routeId.startsWith('/posts')) {
return [['loader', 'component']]
}
// All other routes use defaultBehavior
},
},
}),
],
})
Precedence Order
- Per-route
codeSplitGroupings(highest) splitBehaviorfunctiondefaultBehavioroption (lowest)
Common Mistakes
1. HIGH: Exporting component functions prevents code splitting
// WRONG — export puts PostsComponent in the main bundle
export function PostsComponent() {
return <div>Posts</div>
}
// CORRECT — no export, function stays in the split chunk
function PostsComponent() {
return <div>Posts</div>
}
2. MEDIUM: Trying to code-split the root route
__root.tsx does not support code splitting. It is always rendered regardless of the current route. Do not create __root.lazy.tsx.
3. MEDIUM: Splitting the loader adds double async cost
// AVOID unless you have a specific reason
codeSplittingOptions: {
defaultBehavior: [
['loader'], // Fetch chunk THEN execute loader = two network waterfalls
['component'],
],
}
// PREFERRED — loader stays in main bundle (default behavior)
codeSplittingOptions: {
defaultBehavior: [
['component'],
['errorComponent'],
['notFoundComponent'],
],
}
4. HIGH: Importing Route in code-split files for typed hooks
// WRONG — importing Route pulls route config into the lazy chunk
import { Route } from './posts.tsx'
const data = Route.useLoaderData()
// CORRECT — getRouteApi gives typed hooks without pulling in the route
import { getRouteApi } from '@tanstack/react-router'
const routeApi = getRouteApi('/posts')
const data = routeApi.useLoaderData()
Cross-References
- router-core/data-loading — Loader splitting decisions affect data loading performance. Splitting the loader adds latency before data can be fetched.
- router-core/type-safety —
getRouteApiis the type-safe way to access hooks from split files.
FAQ
What does the code-splitting skill do?
Automatic code splitting (autoCodeSplitting), .lazy.tsx convention, createLazyFileRoute, createLazyRoute, lazyRouteComponent, getRouteApi for typed hooks in split files, codeSplitGroupings per-route override, splitBehavior programmatic config, critical vs non-critical properties.
How do I install code-splitting?
Run npx skills add https://github.com/tanstack/router --skill code-splitting in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/code-splitting/ (personal) or .claude/skills/code-splitting/ (project).
Which AI agents can use code-splitting?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is code-splitting safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains code-splitting?
It is published by tanstack in tanstack/router. It has 199 recorded installs.
More from tanstack/router
router-core
tanstack/router
Framework-agnostic core concepts for TanStack Router: route trees, createRouter, createRoute, createRootRoute, createRootRouteWithContext, addChildren, Register type declaration, route matching, route sorting, file naming conventions. Entry point for all router skills.
react-router
tanstack/router
React bindings for TanStack Router: RouterProvider, useRouter, useRouterState, useMatch, useMatches, useLocation, useSearch, useParams, useNavigate, useLoaderData, useLoaderDeps, useRouteContext, useBlocker, useCanGoBack, Link, Navigate, Outlet, CatchBoundary, ErrorComponent. React-specific patterns for hooks, providers, SSR hydration, and createLink with forwardRef.
react-start
tanstack/router
Open the skill to read its SKILL.md instructions.
start-server-core
tanstack/router
Open the skill to read its SKILL.md instructions.
Popular other skills
hyperframes-cli
heygen-com/hyperframes
The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.
triage
mattpocock/skills
Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.
ask-matt
mattpocock/skills
Ask which skill or flow fits your situation. A router over the skills in this repo.
to-questionnaire
mattpocock/skills
Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
loop-me
mattpocock/skills
Grill me about specs for the workflows I want to build, within this workspace.