Skip to main content

Appendix B: Migration Guides

From Create React App to Vite​

Create React App was deprecated in early 2025. Migrate to Vite:

Step 1: Replace Dependencies​

# Remove CRA
pnpm remove react-scripts

# Add Vite
pnpm add -D vite @vitejs/plugin-react

Step 2: Create vite.config.ts​

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
plugins: [react()],
server: { port: 3000 },
});

Step 3: Move index.html​

Move public/index.html to the project root and update it:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

Step 4: Update Environment Variables​

  • Rename REACT_APP_* to VITE_*
  • Replace process.env.REACT_APP_* with import.meta.env.VITE_*

Step 5: Update Scripts​

{
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
}
}

From React Router to TanStack Router​

Step 1: Install TanStack Router​

pnpm remove react-router-dom
pnpm add @tanstack/react-router
pnpm add -D @tanstack/router-plugin

Step 2: Convert Route Definitions​

React Router:

<Routes>
<Route path="/" element={<Home />} />
<Route path="/projects" element={<Projects />} />
<Route path="/projects/:id" element={<ProjectDetail />} />
</Routes>

TanStack Router (file-based):

src/routes/
├── index.tsx → /
├── projects/
│ ├── index.tsx → /projects
│ └── $id.tsx → /projects/:id

Step 3: Convert Hooks​

React RouterTanStack Router
useParams()Route.useParams()
useSearchParams()Route.useSearch()
useNavigate()useNavigate()
<Link to="/path"><Link to="/path">
useLocation()useLocation()

From Zod to Effect Schema​

Type Equivalents​

ZodEffect Schema
z.string()Schema.String
z.number()Schema.Number
z.boolean()Schema.Boolean
z.object({})Schema.Struct({})
z.array(z.string())Schema.Array(Schema.String)
z.enum(["a", "b"])Schema.Literal("a", "b")
z.string().min(1)Schema.String.pipe(Schema.minLength(1))
z.string().optional()Schema.optional(Schema.String)
z.string().nullable()Schema.NullOr(Schema.String)
z.infer<typeof schema>typeof schema.Type
schema.parse(data)Schema.decodeUnknownSync(schema)(data)
schema.safeParse(data)Schema.decodeUnknownEither(schema)(data)

Key Differences​

  1. Pipe syntax: Effect Schema uses .pipe() for chaining refinements instead of method chaining
  2. Branded types: Effect Schema has native branded type support via Schema.brand()
  3. Encoding: Effect Schema supports bidirectional transformations (decode AND encode)
  4. Integration: Effect Schema integrates naturally with Effect's error handling system

From Next.js to TanStack Start​

Key Differences​

ConceptNext.jsTanStack Start
RoutingFile-based (app router)File-based (TanStack Router)
Data fetchingServer Components, fetchRoute loaders, TanStack Query
API routesapp/api/route.tsServer functions
SSRAutomatic (app router)Opt-in per route
DeploymentVercel-optimizedAny Vite-compatible host
Type safetyPartialEnd-to-end

Migration Steps​

  1. Replace next with @tanstack/start and vite
  2. Convert page.tsx files to TanStack Router route files
  3. Replace getServerSideProps / Server Components with route loader functions
  4. Replace Next.js <Link> with TanStack Router <Link>
  5. Replace useRouter() with useNavigate() and Route.useParams()
  6. Replace API routes with server functions or standalone API server
  7. Convert middleware to beforeLoad guards