You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: packages/react-router/skills/lifecycle/migrate-from-react-router/SKILL.md
+51-44Lines changed: 51 additions & 44 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,8 +20,11 @@ sources:
20
20
21
21
This is a step-by-step migration checklist. Each check covers one conversion task. Complete them in order.
22
22
23
-
> **CRITICAL**: If your UI is blank after migration, open the console. Errors like "cannot use useNavigate outside of context" mean React Router imports remain alongside TanStack Router imports. Uninstall `react-router` to surface them as TypeScript errors.
23
+
> **CRITICAL**: If your UI is blank after migration, open the console. Errors like "cannot use useNavigate outside of context" mean React Router imports remain alongside TanStack Router imports. Uninstall `react-router` (and `react-router-dom` if present) to surface them as TypeScript errors.
24
+
>
24
25
> **CRITICAL**: TanStack Router uses `to` + `params` for navigation, NOT template literal paths. Never interpolate params into the `to` string.
26
+
>
27
+
> **NOTE**: React Router v7 recommends importing from `react-router` (not `react-router-dom`). The `react-router-dom` package still exists but just re-exports from `react-router`. Check for imports from both.
-`to` is a route path pattern, NOT an interpolated string
199
202
-`params` is a separate prop with typed values
200
-
- Active class: `className="[&.active]:font-bold"` (automatic `active` data attribute)
203
+
- Active styling: use `activeProps={{ className: 'font-bold' }}` or `data-status="active"`attribute for CSS
201
204
202
205
-[ ]**Convert all `useNavigate` calls**
203
206
@@ -294,6 +297,43 @@ Or from within the route component:
294
297
const { postId } =Route.useParams()
295
298
```
296
299
300
+
## `useLocation` — Common Pitfall
301
+
302
+
-[ ]**Replace `useLocation` with specific hooks**
303
+
304
+
React Router's `useLocation` is heavily used, and TanStack Router has a hook with the same name — but they are NOT equivalent. TanStack Router's `useLocation()` returns the router's current location, which during pending navigations may differ from what's currently rendered. Most React Router `useLocation` usage should be replaced with more specific hooks. See [#3110](https://github.com/TanStack/router/issues/3110).
305
+
306
+
Replace based on what you actually need:
307
+
308
+
```tsx
309
+
// React Router
310
+
import { useLocation } from'react-router'
311
+
const location =useLocation()
312
+
313
+
// ❌ DON'T just swap to TanStack Router's useLocation — it's the "live" URL
TanStack Router (with `autoCodeSplitting: true` in plugin config, this is automatic). For manual splitting:
395
+
-[ ]**Convert lazy route imports** — with `autoCodeSplitting: true` in the plugin config, this is automatic. For manual splitting, use `.lazy.tsx` files:
Copy file name to clipboardExpand all lines: packages/react-router/skills/react-router/SKILL.md
+32-25Lines changed: 32 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -381,6 +381,23 @@ function Nav() {
381
381
}
382
382
```
383
383
384
+
### Reusable Components with Router Hooks
385
+
386
+
To create a component that uses router hooks across multiple routes, pass a union of route paths as the `from` prop:
387
+
388
+
```tsx
389
+
function PostIdDisplay({ from }: { from:'/posts/$id'|'/drafts/$id' }) {
390
+
const { id } =useParams({ from })
391
+
return <span>ID: {id}</span>
392
+
}
393
+
394
+
// Usage in different route components
395
+
<PostIdDisplayfrom="/posts/$id" />
396
+
<PostIdDisplayfrom="/drafts/$id" />
397
+
```
398
+
399
+
This pattern avoids `strict: false` (which returns an imprecise union) while keeping the component reusable across specific known routes.
400
+
384
401
### Auth Provider Must Wrap RouterProvider
385
402
386
403
If routes use auth context (via `createRootRouteWithContext`), the auth provider must be an ancestor of `RouterProvider`:
@@ -420,7 +437,7 @@ const router = createRouter({
420
437
421
438
### 1. HIGH: Using React hooks in `beforeLoad` or `loader`
422
439
423
-
`beforeLoad` and `loader` are NOT React components — they are plain async functions called by the router. React hooks cannot be used in them.
440
+
`beforeLoad` and `loader` are NOT React components — they are plain async functions. React hooks cannot be called in them. Pass auth state via router context instead.
424
441
425
442
```tsx
426
443
// WRONG — useAuth is a React hook, cannot be called here
### 2. HIGH: Wrapping RouterProvider inside an auth provider incorrectly
452
458
453
-
If you use `createRootRouteWithContext<{ auth: AuthState }>()`, the auth state must be available when the router is created — not injected after.
459
+
Create the router once with an `undefined!` placeholder, then inject live auth via `RouterProvider`'s `context` prop. Do NOT recreate the router on auth changes — this resets caches and rebuilds the tree.
454
460
455
461
```tsx
456
-
// WRONG — router created before auth is available
0 commit comments