Skip to main content

tanstack-router

TanStack Router patterns for type-safe, file-based routing. Covers installation, route configuration, typed params/search, layouts, and navigation. Use when setting up routes, implementing navigation, or configuring route loaders.

Informações da origem

Repositório
MadAppGang/claude-code
Última atividade na origem
6 de janeiro de 2026 às 00:53
Idioma detectado do SKILL.md
inglês
Estrelas
286
Forks
24

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
tanstack-router
description
TanStack Router patterns for type-safe, file-based routing. Covers installation, route configuration, typed params/search, layouts, and navigation. Use when setting up routes, implementing navigation, or configuring route loaders.
# TanStack Router Patterns Type-safe, file-based routing for React applications with TanStack Router. ## Installation ```bash pnpm add @tanstack/react-router pnpm add -D @tanstack/router-plugin ``` ```typescript // vite.config.ts import { TanStackRouterVite } from '@tanstack/router-plugin/vite' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [ react(), TanStackRouterVite(), // Generates route tree ], }) ``` ## Automatic Code Splitting (Recommended) **TanStack Router v1.x+ (2025)** introduces automatic code splitting that separates critical route configuration from non-critical components. ```typescript // vite.config.ts import { TanStackRouterVite } from '@tanstack/router-plugin/vite' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [ react(), TanStackRouterVite({ autoCodeSplitting: true, // NEW: Enable automatic splitting }), ], }) ``` **What Gets Split:** | Critical (Always Loaded) | Non-Critical (Lazy Loaded) | |--------------------------|---------------------------| | Route configuration | Component | | Loaders | Error component | | Search params validation | Pending component | | beforeLoad | Not-found component | **Benefits:** - Smaller initial bundle (route config without components) - Automatic optimization (no manual `.lazy.tsx` files needed) - Better perceived performance (loaders start immediately) **When to Use:** - **Recommended for all new projects** - Existing projects: Enable and test bundle sizes - Large apps benefit most (many routes) ## Bootstrap ```typescript // src/main.tsx import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree }) // Register router for type safety declare module '@tanstack/react-router' { interface Register { router: typeof router } } ReactDOM.createRoot(document.getElementById('root')!).render( <StrictMode> <RouterProvider router={router} /> </StrictMode> ) ``` ## File-Based Routes ``` src/routes/ ├── __root.tsx # Root layout (Outlet, providers) ├── index.tsx # "/" route ├── about.tsx # "/about" route ├── users/ │ ├── index.tsx # "/users" route │ └── $userId.tsx # "/users/:userId" route (dynamic) └── posts/ ├── $postId/ │ ├── index.tsx # "/posts/:postId" route │ └── edit.tsx # "/posts/:postId/edit" route └── index.tsx # "/posts" route ``` **Naming Conventions:** - `__root.tsx` - Root layout (contains `<Outlet />`) - `index.tsx` - Index route for that path - `$param.tsx` - Dynamic parameter (e.g., `$userId` → `:userId`) - `_layout.tsx` - Layout route (no URL segment) - `route.lazy.tsx` - Lazy-loaded route ## Virtual File Routes Virtual file routes allow the router to auto-generate route anchors without physical files: ``` src/routes/ ├── __root.tsx # Root layout ├── index.tsx # "/" (physical file) ├── about.lazy.tsx # "/about" (virtual route, lazy only) └── users/ ├── index.tsx # "/users" (physical) └── $userId.lazy.tsx # "/users/:userId" (virtual, lazy only) ``` **Key Insight:** If you only need a component (no loader, no search validation), you can delete the base route file. The router auto-generates a virtual anchor for `.lazy.tsx` files. **Example - Minimal About Page:** ```typescript // src/routes/about.lazy.tsx // No about.tsx needed! Router creates virtual anchor import { createLazyFileRoute } from '@tanstack/react-router' export const Route = createLazyFileRoute('/about')({ component: () => <div>About Us</div>, }) ``` **When Virtual Routes Make Sense:** - Static pages with no data fetching - Simple UI components - When you want maximum code splitting ## Root Layout ```typescript // src/routes/__root.tsx import { createRootRoute, Outlet } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/router-devtools' export const Route = createRootRoute({ component: () => ( <> <nav> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/users">Users</Link> </nav> <main> <Outlet /> {/* Child routes render here */} </main> <TanStackRouterDevtools /> {/* Auto-hides in production */} </> ), }) ``` ## Basic Route ```typescript // src/routes/about.tsx import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/about')({ component: AboutComponent, }) function AboutComponent() { return <div>About Page</div> } ``` ## Dynamic Routes with Params ```typescript // src/routes/users/$userId.tsx import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/users/$userId')({ component: UserComponent, }) function UserComponent() { const { userId } = Route.useParams() // Fully typed! return <div>User ID: {userId}</div> } ``` ## Typed Search Params ```typescript // src/routes/users/index.tsx import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' const userSearchSchema = z.object({ page: z.number().default(1), filter: z.enum(['active', 'inactive', 'all']).default('all'), search: z.string().optional(), }) export const Route = createFileRoute('/users/')({ validateSearch: userSearchSchema, component: UsersComponent, }) function UsersComponent() { const { page, filter, search } = Route.useSearch() // Fully typed! return ( <div> <p>Page: {page}</p> <p>Filter: {filter}</p> {search && <p>Search: {search}</p>} </div> ) } ``` ## Navigation with Link ```typescript import { Link } from '@tanstack/react-router' // Basic navigation <Link to="/about">About</Link> // With params <Link to="/users/$userId" params={{ userId: '123' }}> View User </Link> // With search params <Link to="/users" search={{ page: 2, filter: 'active' }} > Users Page 2 </Link> // With state <Link to="/details" state={{ from: 'home' }}> Details </Link> // Active link styling <Link to="/about" activeProps={{ className: 'text-blue-600 font-bold' }} inactiveProps={{ className: 'text-gray-600' }} > About </Link> ``` ## Programmatic Navigation ```typescript import { useNavigate } from '@tanstack/react-router' function MyComponent() { const navigate = useNavigate() const handleClick = () => { // Navigate to route navigate({ to: '/users' }) // With params navigate({ to: '/users/$userId', params: { userId: '123' } }) // With search navigate({ to: '/users', search: { page: 2 } }) // Replace history navigate({ to: '/login', replace: true }) // Go back navigate({ to: '..' }) // Relative navigation } return <button onClick={handleClick}>Navigate</button> } ``` ## Route Loaders (Data Fetching) **Basic Loader:** ```typescript // src/routes/users/$userId.tsx import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/users/$userId')({ loader: async ({ params }) => { const user = await fetchUser(params.userId) return { user } }, component: UserComponent, }) function UserComponent() { const { user } = Route.useLoaderData() // Fully typed! return <div>{user.name}</div> } ``` **With TanStack Query Integration** (see **tanstack-query** skill for details): ```typescript import { queryClient } from '@/app/queryClient' import { userQueryOptions } from '@/features/users/queries' export const Route = createFileRoute('/users/$userId')({ loader: ({ params }) => queryClient.ensureQueryData(userQueryOptions(params.userId)), component: UserComponent, }) ``` ## Manual Code Splitting (`.lazy.tsx` Pattern) For fine-grained control over code splitting, split routes into critical and lazy files: **Critical Route File (posts.tsx):** ```typescript // src/routes/posts.tsx - Loaded immediately import { createFileRoute } from '@tanstack/react-router' import { postsQueryOptions } from '@/features/posts/queries' import { queryClient } from '@/app/queryClient' export const Route = createFileRoute('/posts')({ // Critical: loaders, search validation, beforeLoad loader: () => queryClient.ensureQueryData(postsQueryOptions()), validateSearch: (search) => postsSearchSchema.parse(search), }) ``` **Lazy Route File (posts.lazy.tsx):** ```typescript // src/routes/posts.lazy.tsx - Code-split, loaded on navigation import { createLazyFileRoute } from '@tanstack/react-router' import { Posts } from '@/features/posts/components/Posts' export const Route = createLazyFileRoute('/posts')({ // Non-critical: component, error/pending/notFound component: Posts, pendingComponent: () => <PostsSkeleton />, errorComponent: ({ error }) => <PostsError error={error} />, }) ``` **Lazy-Only Properties:** - `component` - The main route component - `errorComponent` - Error boundary UI - `pendingComponent` - Loading/suspense UI - `notFoundComponent` - 404 UI for this route **Critical-Only Properties (NOT in lazy):** - `loader` / `loaderDeps` - `beforeLoad` - `validateSearch` - `search` (search middleware) - `context` **Best Practice:** Use automatic code splitting (`autoCodeSplitting: true`) unless you need specific control over what goes in each file. ## Type-Safe Route Data Access (`getRouteApi`) When building components outside the route file, use `getRouteApi` for type-safe access: ```typescript // src/features/posts/components/PostHeader.tsx import { getRouteApi } from '@tanstack/react-router' // Get typed route API without importing the route const routeApi = getRouteApi('/posts/$postId') export function PostHeader() { // All these are fully typed! const { postId } = routeApi.useParams() const { post } = routeApi.useLoaderData() const { view } = routeApi.useSearch() const context = routeApi.useRouteContext() return ( <header> <h1>{post.title}</h1> <span>Viewing: {view}</span> </header> ) } ```
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub