Skip to main content

tanstack-router-migration

Migrate React applications from React Router to TanStack Router with file-based routing. Use when user requests: (1) Router migration, (2) TanStack Router setup, (3) File-based routing implementation, (4) React Router replacement, (5) Type-safe routing, or mentions 'migrate router', 'tanstack router', 'file-based routes'.

소스 정보

저장소
redpanda-data/console
최근 소스 활동
2026년 9월 16일 14:00
감지된 SKILL.md 언어
영어
스타
4,336
포크
431

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
5 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
tanstack-router-migration
description
Migrate React applications from React Router to TanStack Router with file-based routing. Use when user requests: (1) Router migration, (2) TanStack Router setup, (3) File-based routing implementation, (4) React Router replacement, (5) Type-safe routing, or mentions 'migrate router', 'tanstack router', 'file-based routes'.
allowed-tools
Read, Write, Edit, Bash, Glob, Grep, Task
# React Router to TanStack Router Migration Migrate React applications from React Router to TanStack Router with file-based routing. This skill provides a structured approach for both incremental and clean migrations. ## Critical Rules **ALWAYS:** - Use file-based routing with routes in `src/routes/` directory - Use `from` parameter in all hooks for type safety (`useParams({ from: '/path' })`) - Validate search params with Zod schemas using `@tanstack/zod-adapter` - Configure build tool plugin before creating routes - Register router type for full TypeScript inference - Use `fallback()` wrapper for optional search params **NEVER:** - Edit `routeTree.gen.ts` (auto-generated file) - Use React Router hooks in new code during migration - Forget the `from` parameter (loses type safety) - Use string-only validation for search params - Skip the build plugin configuration ## Dependencies ```bash # Core dependencies bun add @tanstack/react-router @tanstack/zod-adapter # Build plugin (choose one based on your bundler) bun add -d @tanstack/router-plugin # Optional integrations bun add nuqs # URL state management bun add @sentry/react # Error tracking with router integration ``` ## Migration Phases ### Phase 1: Assessment **Audit existing React Router usage:** ```bash # Find all React Router imports grep -r "from 'react-router" src/ --include="*.tsx" --include="*.ts" grep -r 'from "react-router' src/ --include="*.tsx" --include="*.ts" # Find hook usages grep -r "useParams\|useSearchParams\|useNavigate\|useLocation\|useMatch" src/ ``` **Document:** - [ ] React Router version (v5 or v6) - [ ] Number of routes - [ ] `useParams` usage count - [ ] `useSearchParams` usage count - [ ] `useNavigate` usage count - [ ] Custom Link components - [ ] Route guards/protected routes - [ ] Existing route structure ### Phase 2: Setup **1. Configure Build Tool** See [references/build-configuration.md](references/build-configuration.md) for full configs. **Rspack/Rsbuild:** ```typescript // rsbuild.config.ts import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack'; export default { tools: { rspack: (config) => { config.plugins?.push( TanStackRouterRspack({ target: 'react', autoCodeSplitting: true, routesDirectory: './src/routes', generatedRouteTree: './src/routeTree.gen.ts', quoteStyle: 'single', semicolons: true, }) ); // Prevent rebuild loop config.watchOptions = { ignored: ['**/routeTree.gen.ts'] }; return config; }, }, }; ``` **Vite:** ```typescript // vite.config.ts import { TanStackRouterVite } from '@tanstack/router-plugin/vite'; export default defineConfig({ plugins: [ TanStackRouterVite({ target: 'react', autoCodeSplitting: true, routesDirectory: './src/routes', generatedRouteTree: './src/routeTree.gen.ts', }), react(), ], }); ``` **2. Configure Linter** ```jsonc // biome.jsonc or eslint config { "files": { "ignore": ["**/routeTree.gen.ts"] }, "overrides": [ { "include": ["**/routes/**/*"], "linter": { "rules": { "style": { "useFilenamingConvention": "off" // Allow $param.tsx naming } } } } ] } ``` **3. Create Routes Directory** ```bash mkdir -p src/routes ``` ### Phase 3: Router Creation **Create Router Instance:** ```typescript // src/app.tsx import { createRouter, RouterProvider } from '@tanstack/react-router'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { routeTree } from './routeTree.gen'; import { NotFoundPage } from './components/misc/not-found-page'; const queryClient = new QueryClient(); const router = createRouter({ routeTree, context: { basePath: getBasePath(), queryClient, }, basepath: getBasePath(), trailingSlash: 'never', defaultNotFoundComponent: NotFoundPage, }); // Register router type for full TypeScript inference declare module '@tanstack/react-router' { interface Register { router: typeof router; } // Extend HistoryState for typed navigation state interface HistoryState { // Add your custom state properties here returnUrl?: string; documentId?: string; documentName?: string; } } export function App() { return ( <QueryClientProvider client={queryClient}> <RouterProvider router={router} /> </QueryClientProvider> ); } ``` **Define Router Context Type:** ```typescript // src/routes/__root.tsx import type { QueryClient } from '@tanstack/react-query'; export type RouterContext = { basePath: string; queryClient: QueryClient; }; ``` ### Phase 4: Route Migration **Create Root Layout:** ```typescript // src/routes/__root.tsx import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'; import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'; import type { QueryClient } from '@tanstack/react-query'; import { NuqsAdapter } from 'nuqs/adapters/tanstack-router'; export type RouterContext = { basePath: string; queryClient: QueryClient; }; export const Route = createRootRouteWithContext<RouterContext>()({ component: RootLayout, }); function RootLayout() { return ( <> <NuqsAdapter> <ErrorBoundary> <AppLayout> <Outlet /> </AppLayout> </ErrorBoundary> </NuqsAdapter> {process.env.NODE_ENV === 'development' && ( <TanStackRouterDevtools position="bottom-right" /> )} </> ); } ``` **File-Based Route Structure:** ``` src/routes/ ├── __root.tsx # Root layout ├── index.tsx # / (root redirect) ├── overview/ │ └── index.tsx # /overview ├── topics/ │ ├── index.tsx # /topics │ └── $topicName/ │ ├── index.tsx # /topics/:topicName │ └── edit.tsx # /topics/:topicName/edit ├── security/ │ ├── index.tsx # /security (redirect) │ ├── acls/ │ │ ├── index.tsx # /security/acls │ │ ├── create.tsx # /security/acls/create │ │ └── $aclName/ │ │ └── details.tsx # /security/acls/:aclName/details ``` See [references/route-templates.md](references/route-templates.md) for complete templates. ### Phase 5: Hook Migration | React Router | TanStack Router | |--------------|-----------------| | `useParams()` | `useParams({ from: '/path/$param' })` | | `useSearchParams()` | `routeApi.useSearch()` with Zod validation | | `useNavigate()` | `useNavigate({ from: '/path' })` | | `useLocation()` | `useLocation()` (same API) | | `<Link to="/path">` | `<Link to="/path">` (type-safe) | | `<Navigate to="/path" />` | `<Navigate to="/path" />` | See [references/migration-patterns.md](references/migration-patterns.md) for detailed before/after examples. **Navigation State:** Pass typed state between routes using `HistoryState`: ```typescript // Navigating with state const navigate = useNavigate(); navigate({ to: '/documents/$documentId', params: { documentId }, state: { returnUrl: location.pathname, documentName: 'My Document', }, }); // Reading state in destination component import { useLocation } from '@tanstack/react-router'; function DocumentPage() { const location = useLocation(); const { returnUrl, documentName } = location.state; // Use state values... } ``` **useParams Migration:** ```typescript // Before (React Router) import { useParams } from 'react-router-dom'; const { id } = useParams<{ id: string }>(); // After (TanStack Router) import { useParams } from '@tanstack/react-router'; const { id } = useParams({ from: '/items/$id' }); ``` **useSearch with Zod Validation:** ```typescript // In route file import { fallback, zodValidator } from '@tanstack/zod-adapter'; import { z } from 'zod'; const searchSchema = z.object({ tab: fallback(z.string().optional(), undefined), page: fallback(z.number().optional(), 1), q: fallback(z.string().optional(), undefined), }); export const Route = createFileRoute('/items/')({ validateSearch: zodValidator(searchSchema), component: ItemsPage, }); // In component import { getRouteApi, useNavigate } from '@tanstack/react-router'; const routeApi = getRouteApi('/items/'); function ItemsPage() { const { tab, page, q } = routeApi.useSearch(); const navigate = useNavigate({ from: '/items/' }); const handleTabChange = (newTab: string) => { navigate({ search: (prev) => ({ ...prev, tab: newTab }) }); }; } ``` ### Phase 6: Testing **Create Test Utilities:** ```typescript // src/test-utils.tsx import { createMemoryHistory, createRouter, RouterProvider } from '@tanstack/react-router'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { render, type RenderOptions } from '@testing-library/react'; import { routeTree } from './routeTree.gen'; import type { RouterContext } from './routes/__root'; interface RenderWithFileRoutesOptions extends Omit<RenderOptions, 'wrapper'> { initialLocation?: string; routerContext?: Partial<RouterContext>; } export function renderWithFileRoutes( ui: React.ReactElement | null = null, { initialLocation = '/', routerContext = {}, ...renderOptions }: RenderWithFileRoutesOptions = {} ) { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); const router = createRouter({ routeTree, history: createMemoryHistory({ initialEntries: [initialLocation] }), context: { basePath: '', queryClient, ...routerContext }, }); function Wrapper({ children }: { children: React.ReactNode }) { return ( <QueryClientProvider client={queryClient}> <RouterProvider router={router}>{children}</RouterProvider> </QueryClientProvider> ); } return { ...render(ui ?? <div />, { wrapper: Wrapper, ...renderOptions }), router, }; } export async function renderRoute(location: string, options?: RenderWithFileRoutesOptions) { const result = renderWithFileRoutes(null, { initialLocation: location, ...options }); await result.router.load(); return result; } ``` **Configure Rstest:** ```typescript // rstest.config.integration.ts import { pluginReact } from '@rsbuild/plugin-react'; import { defineProject } from '@rstest/core'; export default defineProject({ plugins: [pluginReact()], include: ['src/**/*.test.tsx'], testEnvironment: { name: 'happy-dom',
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기