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'.

Quellinformationen

Repository
redpanda-data/console
Letzte Quellaktivität
16. September 2026 um 14:00
Erkannte Sprache von SKILL.md
Englisch
Sterne
4.336
Forks
431

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
5 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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',
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen