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.

معلومات المصدر

المستودع
MadAppGang/claude-code
آخر نشاط في المصدر
٦ يناير ٢٠٢٦ في ٠٠:٥٣
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٨٦
التفرعات
٢٤

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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> ) } ```
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub