Lesson 8 — apply everything on a codebase you own (wisdom track)
Lessons 1–7 taught you the primitives in a sandbox. Wisdom — real retention — only comes from applying them on a messy, real codebase. This lesson is a guided migration playbook: take a Next.js app you own, convert it route-by-route to TanStack Router, and discover the edge cases no lesson can predict.
Knowledge and skills you've built in isolation feel solid. Real apps break that illusion:
You will hit surprises. That's the point. Each one is a learning record waiting to be written.
Six phases. Do not skip ahead — each builds on the previous. Expect each phase to surface questions worth bringing back to your teacher.
Install TanStack Router + the Vite plugin (or your bundler's equivalent). The plugin watches your routes/ folder and generates routeTree.gen.ts — the typed route tree that powers all the type safety from Lessons 5–7.
npm install @tanstack/react-router
npm install -D @tanstack/router-plugin vite
Wire the plugin in vite.config.ts, then create your router in a router.ts file pointing at the generated route tree. You can run Next.js and TanStack side-by-side during migration — they don't have to swap in one shot.
Before touching files, draw the route tree. For each Next.js route:
$param._name).(name)).Translate Next.js conventions to TanStack (recap from Lessons 1–4):
| Next.js | TanStack |
|---|---|
app/.../page.tsx | routes/.../index.tsx |
app/.../layout.tsx | routes/.../route.tsx with <Outlet/> |
app/[slug]/page.tsx | routes/$slug/index.tsx |
app/(group)/page.tsx | routes/(group)/index.tsx |
app/[...slug]/page.tsx | routes/$/index.tsx (splat, _splat) |
Pick the smallest leaf route in your app — a static page like /about. Convert it fully:
routes/about/index.tsxcreateFileRoute('/about')({ component: AboutPage })page.tsx into the component/about via the TanStack routerOne green route proves the pipeline. Resist converting everything at once.
For each route with data (Lesson 7):
loader + Route.useLoaderData() in the componentuseEffect + fetch → same; delete the effect, the loading state, the error stateFor params-dependent routes, declare loader: ({ params, signal }) => .... For search-dependent routes, add validateSearch + loaderDeps.
Find every <Link href="..."> and router.push(...). Replace:
// Before (Next.js)
<Link href={`/posts/${post.id}`}>{post.title}</Link>
router.push(`/posts/${newId}`)
// After (TanStack)
<Link to="/posts/$postId" params={{ postId: post.id }}>{post.title}</Link>
navigate({ to: '/posts/$postId', params: { postId: newId } })
The conversion is mechanical but catches bugs: any broken string interpolation surfaces as a TypeScript error now.
For each useState that holds shareable UI state — filters, pagination, modal-open, selected tab — ask: should this be in the URL? If yes:
validateSearch schema with .catch() fallbacksRoute.useSearch()navigate({ search: prev => ({ ...prev, ... }) })useStateThis is the highest-leverage refactor — shareable URLs and natural back-button behavior for free.
loading.tsx → pendingComponent
Next.js's loading.tsx convention maps to TanStack's route pendingComponent option, not a separate file. Same for error.tsx → errorComponent.
app/api/*), and server actions are orthogonal — keep them in whatever server layer you adopt.
next/dynamic calls become unnecessary — delete them and let the router handle splitting per route.
routes/ and generates routeTree.gen.ts — the typed route tree that powers all type safety in navigation, params, and loaders.page.tsx maps to which TanStack file?index.tsx is the exact-match leaf (like page.tsx). route.tsx is the layout (like layout.tsx).layout.tsx maps to?route.tsx is a segment's layout wrapper — renders <Outlet/> for children. That's layout.tsx's role./about). One green route proves the pipeline end-to-end before you tackle complexity.loader (fetches) + a component that reads via useLoaderData().loading.tsx maps to which option?loading.tsx → route's pendingComponent option. Same for error.tsx → errorComponent. Options, not separate files.useState to URL search params gives shareable URLs and natural back-button for free.Primary source: TanStack Router — Installation for the Vite plugin setup, then your own codebase. The migration is the curriculum.