nextjs-to-tanstack
Migrate a Next.js app (App Router and common Pages Router usage) to TanStack Start and TanStack Router file routes.
The workflow rewrites routes and handlers, updates tooling, and writes TANSTACK_MIGRATION_NEXT_STEPS.md next to package.json for remaining manual work.
Quick start
bash
Back up or commit first. Many files change; unmigrated pages/ files may end up under migrated-from-pages/.
Workflow params
Pass these when running workflow.yaml with -p:
| Param | Type | Default | Meaning |
|---|---|---|---|
enableAiFollowupFixups | "true" | "false" | "false" | Optional AI pass after deterministic steps (see below). |
enableAiFollowupFixups — If "true", an AI pass tries to clear low-risk // TODO: markers and tasks in TANSTACK_MIGRATION_NEXT_STEPS.md. Output is not fully deterministic; turn on only if you want that extra step.
bash
What runs (overview)
Layout and files
- Adds a Vite / TanStack Start scaffold (e.g.
vite.config.ts, router entry). If Next i18n is detected, hints may go under.codemod/. - Removes root Next-only configs (
next.config.*,postcss.config.*). - Maps layouts to
__root.tsx,page/routemodules tocreateFileRoute, and API-style routes to TanStack server handlers. - Handles dynamic segments,
loading/error/not-found/templatefiles, and prunes empty App Router segment folders.
Next.js APIs (mechanical rewrites)
metadata/viewport→ routehead();params/searchParams→ router hooks.next/link,next/image,next/navigation,next/dynamic,next/script→ TanStack-friendly patterns where safe.next/cache→ TanStack Query–style invalidation;next/headers→ Start server helpers;next/server→ FetchRequest/Response(gaps documented in the generated guide).next/og→ satori + resvg;opengraph-image/twitter-image→ serverGETroutes.
Data, types, and polish
- Safe top-level
awaitmay move intoRoute.loader; harder cases get// TODO:(R10). - Pages data exports (
getStaticProps, …) are stripped from migrated routes; Next-only types often become placeholders you should fix. - Fonts and
globals.cssget patches; unusednext/*imports are dropped; survivors are annotated (R10b).
Package, tooling, and exit
package.jsonpicks up Start / Router / Vite deps and scripts.- tsconfig, ESLint, and doc/CI strings that assume Next get light patches.
- Leftover
pages/may move tomigrated-from-pages/;TANSTACK_MIGRATION_NEXT_STEPS.mdis written next topackage.json. - Optional AI step if enabled; transient codemod state (e.g.
.codemod/state.json) is removed afterward.
Monorepos
Globs match nested trees (e.g. apps/foo/app/...). In large repos, still pass -t at the Next package root so the run stays fast and focused.
Supplementary notes
- Pipeline — Steps run in a fixed order. Later passes assume earlier renames (e.g.
[slug]/page.tsx→$slug.tsx) already happened, so re-running individual scripts by hand out of order may not match a full workflow run. - Two outputs to trust — Inline
// TODO: …comments mark uncertain spots;TANSTACK_MIGRATION_NEXT_STEPS.mdrolls those themes into a single checklist with doc links. - Surface area — The codemod targets common App Router and Pages Router paths (
app/**,pages/**, shared components). Custom indirection (barrels, codegen, or non-standard folders) may need manual follow-up even when imports are valid.
Before and after (illustrative)
The snippets below mirror fixture tests in this repo. Your files may include extra imports, types, or TODOs until you finish the checklist.
Dynamic page → TanStack file route
Before — src/app/posts/[slug]/page.tsx
tsx
After — src/app/posts/$slug.tsx (dynamic segment becomes a $param file name; URL shape is createFileRoute("/posts/$slug"))
tsx
App Router API route → server GET handler
Before — src/app/api/hello/route.ts
tsx
After — src/app/api/hello.ts (file moves up; HTTP methods live under server.handlers)
tsx
Root layout → __root.tsx
Before — src/app/layout.tsx
tsx
After — src/app/__root.tsx (shell uses Outlet, HeadContent, and Scripts)
tsx
Later passes move metadata toward route head() and clean up remaining next-only types — expect to edit this file again while you work through TANSTACK_MIGRATION_NEXT_STEPS.md.
Link: next/link → TanStack Router
Before
tsx
After
tsx
Redirects (server / loader style)
Before — next/navigation
tsx
After — throw redirect from @tanstack/react-router (appropriate in loaders, beforeLoad, and server code paths; client components use useNavigate() instead)
tsx
Some call sites may still get a TODO comment if the codemod cannot prove the context is safe to rewrite.
After you run
-
Install — Install deps so
package.jsonedits take effect. Refresh lockfiles as needed; skip strict frozen installs until things stabilize. Watch for packages that still peer on Next. -
Open the checklist — Read
TANSTACK_MIGRATION_NEXT_STEPS.md: i18n, env (NEXT_PUBLIC_*→VITE_*/import.meta.env), OG URLs, TODO buckets (R10, R4e–R4i, …). It links to the official Migrate from Next.js guide. -
Sweep leftovers — e.g.
rg '// TODO:', remainingfrom "next/…"imports,middleware, andmigrated-from-pages/after you merge or delete what you need. -
Run the dev server —
npm run devorvite devfrom the package root; fix navigation, loaders, and tests (Vitest: see R4h-bis in the guide).
This codemod is best-effort. Edge runtime, uncommon Next APIs, and app-specific design may still need manual work — the guide and TODOs are the source of truth.
Development
bash
Resources
- Migrate from Next.js (TanStack Start)
- TanStack Router — routing concepts
- TanStack Start — server routes
License
MIT