---
# === IDENTITY ===
id: software/migrations/cra-to-nextjs/2026
canonical_question: "How do I migrate from Create React App to Next.js?"
aliases:
  - "CRA to Next.js migration guide"
  - "migrate create-react-app to nextjs"
  - "convert CRA to Next.js App Router"
  - "replace react-scripts with next"
  - "create react app deprecated next.js migration"
  - "move from CRA to Next.js"
  - "react-scripts to next.js"
  - "CRA to Next.js 16 migration"
  - "CRA to Next.js 16.2 migration"
  - "migrate create-react-app to Next.js with Cache Components"
entity_type: software_reference
domain: software > migrations > cra_to_nextjs
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.94
version: 2.1
first_published: 2026-02-17

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: "Next.js 16.x security release (2026-05) — 13 CVEs patched across DoS, proxy bypass, SSRF, cache poisoning, XSS; Next.js 16.2 (2026-03) ships 400% faster dev server"
  next_review: 2026-11-13
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Next.js 16 requires Node.js >= 20.9.0 — Node.js 18 is no longer supported"
  - "Next.js 16 requires TypeScript >= 5.1 — older versions will fail to type-check"
  - "output: 'export' disables useParams, API routes, proxy (middleware), and all server-side features — remove it once SSR is needed"
  - "params, searchParams, cookies(), headers(), and draftMode() are now fully async Promises in Next.js 16 — synchronous access removed (was deprecated in 15)"
  - "Turbopack is default in Next.js 16 for both dev and build — custom webpack configs require --webpack flag on both next dev and next build"
  - "revalidateTag() now requires a cacheLife profile as a second argument in Next.js 16 — single-argument form is deprecated"
  - "Global CSS can only be imported in app/layout.tsx (or layout files) — importing in page or component files causes a build error"
  - "Pin Next.js patch version to >= the May 2026 security release (covers 13 CVEs including SSRF, cache poisoning, proxy bypass) — older 16.x and 15.x patches are vulnerable"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User wants to migrate CRA to Vite (not Next.js)"
    use_instead: "software/migrations/webpack-to-vite/2026"
  - condition: "User wants to migrate between Next.js versions (e.g., Pages Router to App Router)"
    use_instead: "software/migrations/nextjs-pages-to-app-router/2026"
  - condition: "User is migrating a React Native/Expo app"
    use_instead: "Search for Expo migration guides — this unit covers web CRA only"

# === AGENT HINTS ===
inputs_needed:
  - key: ssr_needed
    question: "Does the app need server-side rendering, API routes, or middleware?"
    type: choice
    options: ["Yes — need SSR/API routes", "No — pure SPA/static export is fine", "Not sure yet"]
  - key: router_type
    question: "Does the CRA app use react-router-dom?"
    type: choice
    options: ["Yes — react-router-dom v5 or v6", "No — no client-side routing", "Custom routing solution"]
  - key: custom_webpack
    question: "Does the CRA project use ejected webpack config or CRACO?"
    type: choice
    options: ["Yes — ejected or CRACO", "No — stock CRA", "Not sure"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/migrations/cra-to-nextjs/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-05-17)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/migrations/webpack-to-vite/2026"
      label: "Webpack to Vite Migration"
    - id: "software/migrations/javascript-to-typescript/2026"
      label: "JavaScript to TypeScript Migration"
  alternative_to:
    - id: "software/migrations/webpack-to-vite/2026"
      label: "Webpack to Vite Migration (alternative path away from CRA without adopting SSR)"
  solves:
    - id: "software/migrations/react-classes-to-hooks/2026"
      label: "React Class Components to Hooks"
  often_confused_with:
    - id: "software/migrations/jquery-to-react/2026"
      label: "jQuery to React Migration (this card is for CRA→Next.js, not jQuery→React)"

# === SOURCES (8 authoritative sources) ===
# Types: official_docs, technical_blog, rfc_spec, academic_paper, community_resource, industry_report
# Reliability: high, moderate_high, moderate, moderate_low, low, authoritative
sources:
  - id: src1
    title: "Migrating from Create React App"
    author: Next.js
    url: https://nextjs.org/docs/app/guides/migrating/from-create-react-app
    type: official_docs
    published: 2026-05-13
    reliability: authoritative
  - id: src2
    title: "CRA to App Router in 5 Steps: A case study with Graphite"
    author: Graphite
    url: https://graphite.com/blog/cra-to-approuter
    type: technical_blog
    published: 2025-06-15
    reliability: high
  - id: src3
    title: "Sunsetting Create React App"
    author: React Team
    url: https://react.dev/blog/2025/02/14/sunsetting-create-react-app
    type: official_docs
    published: 2025-02-14
    reliability: authoritative
  - id: src4
    title: "Migrating from Create React App to NextJS: A Practical Guide"
    author: GeeksforGeeks
    url: https://www.geeksforgeeks.org/reactjs/migrating-from-create-react-app-to-nextjs-a-practical-guide/
    type: community_resource
    published: 2025-03-10
    reliability: moderate_high
  - id: src5
    title: "Upgrading: Version 16"
    author: Next.js
    url: https://nextjs.org/docs/app/guides/upgrading/version-16
    type: official_docs
    published: 2026-05-13
    reliability: authoritative
  - id: src6
    title: "Migrating: App Router"
    author: Next.js
    url: https://nextjs.org/docs/app/guides/migrating/app-router-migration
    type: official_docs
    published: 2026-05-13
    reliability: authoritative
  - id: src7
    title: "Next.js Migrating from React Router"
    author: GeeksforGeeks
    url: https://www.geeksforgeeks.org/reactjs/next-js-migrating-from-react-router/
    type: community_resource
    published: 2025-01-20
    reliability: moderate_high
  - id: src8
    title: "Next.js 16"
    author: Vercel (Jimmy Lai, Josh Story, Sebastian Markbåge, Tim Neutkens)
    url: https://nextjs.org/blog/next-16
    type: official_docs
    published: 2025-10-21
    reliability: authoritative
  - id: src9
    title: "Next.js May 2026 security release"
    author: Vercel
    url: https://vercel.com/changelog/next-js-may-2026-security-release
    type: official_docs
    published: 2026-05-08
    reliability: authoritative
---

# How to Migrate from Create React App to Next.js

## TL;DR

- **Bottom line**: Install `next`, create an `app/` directory with a root layout and optional catch-all `[[...slug]]` page, move your CRA entry point into a `'use client'` dynamic import (`ssr: false`), then incrementally adopt file-based routing, server components, and SSR to replace `react-scripts`. Use `npx @next/codemod@canary upgrade latest` for automated migration to Next.js 16.
- **Key tool/command**: `npm install next@latest && npx next dev` (Next.js 16.2+ is current as of May 2026 — runs Turbopack by default with 400% faster startup vs 16.0).
- **Watch out for**: Accessing `window`/`document` in components that Next.js renders on the server — wrap browser-only code in `useEffect` or dynamic imports with `ssr: false`. Next.js 16 makes all request APIs (`params`, `searchParams`, `cookies()`, `headers()`, `draftMode()`) fully async — synchronous access is removed. `revalidateTag()` now requires a `cacheLife` profile as a second argument.
- **Works with**: Next.js 15+/16.2+ (App Router), React 18+/19.2, Node.js 20.9+ (18 dropped in Next.js 16), TypeScript 5.1+. CRA any version (react-scripts 4.x/5.x). Pin to the May 2026 security patch or later.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Next.js 16 requires Node.js >= 20.9.0 -- Node.js 18 is no longer supported. Verify with `node --version` before starting. [src5, src8]
- Next.js 16 requires TypeScript >= 5.1. Older versions fail to type-check. [src8]
- `output: 'export'` disables `useParams`, API routes, proxy (formerly middleware), and all server-side features. Remove it only after confirming the deployment target supports Node.js server. [src1]
- `params`, `searchParams`, `cookies()`, `headers()`, and `draftMode()` are fully async Promises in Next.js 16 -- synchronous access was removed. All page/layout components receiving these must `await` them. [src5, src8]
- Turbopack is the default bundler in Next.js 16 for both `dev` and `build`. If the project has a custom `webpack` config (from CRA eject or CRACO), the build will fail. Use `--webpack` flag on both `next dev` and `next build` to opt out, or migrate the config. [src5, src8]
- `revalidateTag()` now requires a `cacheLife` profile as a second argument in Next.js 16 (e.g., `revalidateTag('tag', 'max')`). The single-argument form is deprecated and produces a TypeScript error. For read-your-writes semantics in Server Actions, use the new `updateTag(tag)` API. [src8]
- Global CSS can only be imported in `app/layout.tsx` (or layout files). Importing global CSS in page or component files causes a build error. [src1]
- Never let react-router-dom and Next.js file-based routing handle the same route simultaneously -- use the catch-all `[[...slug]]` approach during incremental migration. [src2]
- Pin Next.js to the May 2026 security release or later. That release patched 13 CVEs across denial of service, middleware/proxy bypass, SSRF, cache poisoning, and XSS -- both 16.x and 15.x patch lines received fixes. [src9]

## Quick Reference

| CRA Pattern | Next.js Equivalent | Example |
|---|---|---|
| `react-scripts start` | `next dev` | `npm run dev` uses Turbopack by default (Next.js 16) |
| `react-scripts build` | `next build` | Outputs to `.next/` (or `build/` with `distDir`); Turbopack default in 16 |
| `public/index.html` | `app/layout.tsx` (Root Layout) | Metadata API replaces manual `<head>` tags |
| `src/index.tsx` (ReactDOM.render) | `app/page.tsx` + `'use client'` wrapper | Entry point becomes a Server Component page |
| `react-router-dom` `<Route>` | File-based routing (`app/{route}/page.tsx`) | `app/dashboard/page.tsx` = `/dashboard` |
| `<Link to="/path">` | `<Link href="/path">` | `import Link from 'next/link'` |
| `useNavigate()` / `useHistory()` | `useRouter()` from `next/navigation` | `const router = useRouter(); router.push('/path')` |
| `useParams()` (react-router) | `useParams()` from `next/navigation` | Dynamic route: `app/users/[id]/page.tsx` |
| `REACT_APP_*` env vars | `NEXT_PUBLIC_*` env vars | `NEXT_PUBLIC_API_URL` exposed to browser |
| `proxy` in `package.json` | `rewrites()` in `next.config.ts` | `{ source: '/api/:path*', destination: 'http://backend:3001/:path*' }` |
| `import img from './image.png'` (string) | `import img from './image.png'` (object with `.src`) | Use `<img src={img.src} />` or `<Image src={img} />` |
| CSS Modules (`*.module.css`) | CSS Modules (same, built-in) | No change needed |
| `import './global.css'` in `index.tsx` | `import './global.css'` in `app/layout.tsx` | Global CSS must be imported in layout |
| `reportWebVitals()` | Built-in Next.js analytics or `@next/third-parties` | Remove CRA's web vitals setup |
| Custom webpack in `eject` / `craco` | `webpack` key in `next.config.ts` (requires `--webpack` flag in Next.js 16) | `webpack: (config) => { ... return config }` |
| `homepage` field in `package.json` | `basePath` in `next.config.ts` | `basePath: '/my-app'` |
| `middleware.ts` (Next.js 15) | `proxy.ts` (Next.js 16) | Renamed: export function `proxy(request)` instead of `middleware(request)` |

## Decision Tree

```
START
├── Is the CRA app purely client-side (SPA) with no SSR needs?
│   ├── YES → Use output: 'export' in next.config.ts for static SPA (Step 2)
│   └── NO ↓
├── Does the app use react-router-dom?
│   ├── YES → Phase 1: Keep react-router via catch-all route, Phase 2: Migrate to file-based routing
│   └── NO ↓
├── Does the app need server-side rendering or API routes?
│   ├── YES → Remove output: 'export', create API routes in app/api/, use Server Components
│   └── NO ↓
├── Does the app use CRACO or ejected webpack config?
│   ├── YES → Use --webpack flag in Next.js 16, map custom webpack plugins to next.config.ts
│   └── NO ↓
├── Does the app rely heavily on window/document APIs?
│   ├── YES → Wrap those components with dynamic(() => import(...), { ssr: false })
│   └── NO ↓
├── Targeting Next.js 16 specifically?
│   ├── YES → Ensure Node.js >= 20.9, await all params/searchParams, rename middleware.ts to proxy.ts
│   └── NO (Next.js 15) → Synchronous params still work with deprecation warnings
└── DEFAULT → Follow the 8-step migration below, start with SPA mode, adopt features incrementally
```

## Step-by-Step Guide

### 1. Install Next.js and update scripts

Add Next.js alongside your existing CRA dependencies. Do not remove `react-scripts` yet -- both can coexist during migration. [src1]

```bash
npm install next@latest
```

Update `package.json` scripts:

```json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "dev:old": "react-scripts start"
  }
}
```

Add `.next`, `.next/dev`, and `next-env.d.ts` to `.gitignore`. (Next.js 16 uses `.next/dev` for development output.)

**Verify**: `npx next --version` shows the installed version (15.x+ or 16.x+).

### 2. Create next.config.ts

Create the configuration file at your project root. Start with static export mode to match CRA's SPA behavior. [src1]

```typescript
// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  output: 'export',   // Static SPA mode (matches CRA behavior)
  distDir: 'build',   // Match CRA's output directory
}

export default nextConfig
```

**Note**: If you have a custom webpack config from CRA and are using Next.js 16, Turbopack is the default bundler. Add `--webpack` to your build script or migrate to Turbopack-compatible options. [src5]

**Verify**: File exists at project root alongside `package.json`.

### 3. Create the root layout

Replace `public/index.html` with `app/layout.tsx`. This is the root layout that wraps every page. [src1, src4]

```tsx
// app/layout.tsx
import type { Metadata } from 'next'
import '../src/index.css'  // Import global CSS here

export const metadata: Metadata = {
  title: 'My App',
  description: 'Migrated from CRA to Next.js',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <div id="root">{children}</div>
      </body>
    </html>
  )
}
```

**Verify**: Compare with your `public/index.html` -- all `<meta>`, `<link>`, and `<title>` tags should be accounted for via the Metadata API or direct inclusion.

### 4. Create the catch-all entrypoint page

Use an optional catch-all route to handle all URLs, preserving your existing client-side router (react-router-dom). [src1, src2]

```tsx
// app/[[...slug]]/page.tsx
import { ClientOnly } from './client'

export function generateStaticParams() {
  return [{ slug: [''] }]
}

export default function Page() {
  return <ClientOnly />
}
```

```tsx
// app/[[...slug]]/client.tsx
'use client'

import dynamic from 'next/dynamic'

const App = dynamic(() => import('../../src/App'), { ssr: false })

export function ClientOnly() {
  return <App />
}
```

**Verify**: `npm run dev` → app loads at `http://localhost:3000` with existing routing intact.

### 5. Rename REACT_APP_ environment variables to NEXT_PUBLIC_

Next.js requires the `NEXT_PUBLIC_` prefix for client-side environment variables. [src1]

```bash
# .env
# BEFORE
REACT_APP_API_URL=https://api.example.com
REACT_APP_GA_ID=UA-12345

# AFTER
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_GA_ID=UA-12345
```

Update all references in your source code:

```typescript
// BEFORE
const apiUrl = process.env.REACT_APP_API_URL;

// AFTER
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
```

**Verify**: `grep -rn 'REACT_APP_' --include='*.ts' --include='*.tsx' --include='*.js'` returns zero results.

### 6. Fix static image imports

CRA image imports return a string URL. Next.js image imports return an object with a `.src` property. [src1]

```tsx
// BEFORE (CRA): import returns string
import logo from './logo.png'
<img src={logo} alt="Logo" />

// AFTER (Next.js): import returns object
import logo from './logo.png'
<img src={logo.src} alt="Logo" />

// OR use Next.js Image component for optimization
import Image from 'next/image'
import logo from './logo.png'
<Image src={logo} alt="Logo" />
```

**Verify**: All images render correctly. Check browser DevTools Network tab for 404s on image requests.

### 7. Replace CRA proxy with Next.js rewrites

If your CRA `package.json` has a `proxy` field, replace it with Next.js rewrites. [src1]

```typescript
// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'http://localhost:3001/api/:path*',
      },
    ]
  },
}

export default nextConfig
```

**Note**: In Next.js 16, if you need request-level middleware logic, create a `proxy.ts` file (renamed from `middleware.ts`). [src5]

**Verify**: API requests from the browser are proxied correctly. Check the Network tab for proper responses.

### 8. Clean up CRA artifacts and uninstall react-scripts

Once the app runs successfully on Next.js, remove CRA-specific files and dependencies. [src1, src4]

```bash
# Remove CRA dependencies
npm uninstall react-scripts

# Delete CRA-specific files
rm public/index.html
rm src/index.tsx          # or src/index.js
rm src/react-app-env.d.ts
rm src/reportWebVitals.ts
rm src/setupTests.ts      # if migrating tests separately
```

**Verify**: `npm run build` completes without errors. `npm run dev` starts the Next.js dev server cleanly.

## Code Examples

### TypeScript/Next.js: Migrating a React Router app to file-based routing

> Full script: [typescript-next-js-migrating-a-react-router-app-to.tsx](scripts/typescript-next-js-migrating-a-react-router-app-to.tsx) (46 lines)

```tsx
// Input:  CRA app with react-router-dom routes
// Output: Equivalent Next.js App Router file structure
// BEFORE: CRA with react-router-dom (src/App.tsx)
// import { BrowserRouter, Routes, Route } from 'react-router-dom';
// import Home from './pages/Home';
# ... (see full script)
```

### TypeScript/Next.js: Converting CRA navigation patterns

> Full script: [typescript-next-js-converting-cra-navigation-patte.tsx](scripts/typescript-next-js-converting-cra-navigation-patte.tsx) (38 lines)

```tsx
// Input:  CRA components using react-router hooks
// Output: Next.js equivalents using next/navigation
// BEFORE: CRA with react-router-dom
// import { useNavigate, useParams, useLocation } from 'react-router-dom';
//
# ... (see full script)
```

### TypeScript/Next.js: Wrapping browser-only code for SSR safety

> Full script: [typescript-next-js-wrapping-browser-only-code-for-.tsx](scripts/typescript-next-js-wrapping-browser-only-code-for-.tsx) (36 lines)

```tsx
// Input:  CRA component that uses window/document/localStorage
// Output: Next.js component with SSR-safe browser API access
'use client'
import { useEffect, useState } from 'react'
import dynamic from 'next/dynamic'
# ... (see full script)
```

### TypeScript/Next.js: Async params pattern (Next.js 16)

```tsx
// Input:  Next.js 15 page component with synchronous params
// Output: Next.js 16 page component with async params (required)

// BEFORE (Next.js 15 — synchronous, deprecated):
// export default function UserPage({ params }: { params: { id: string } }) {
//   return <h1>User {params.id}</h1>
// }

// AFTER (Next.js 16 — async params required):
export default async function UserPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return <h1>User {id}</h1>
}

// With searchParams (also async in Next.js 16):
export default async function SearchPage({
  searchParams,
}: {
  searchParams: Promise<{ q?: string }>
}) {
  const { q } = await searchParams
  return <h1>Results for: {q}</h1>
}
```

## Anti-Patterns

### Wrong: Importing global CSS in a page component

```tsx
// ❌ BAD — Next.js only allows global CSS imports in layout.tsx
// app/dashboard/page.tsx
import '../../styles/global.css'  // Error: Global CSS cannot be imported from components

export default function Dashboard() {
  return <h1>Dashboard</h1>
}
```

### Correct: Import global CSS in the root layout only

```tsx
// ✅ GOOD — Global CSS imported in app/layout.tsx
// app/layout.tsx
import '../styles/global.css'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
```

### Wrong: Using react-router Link syntax in Next.js

```tsx
// ❌ BAD — react-router-dom Link uses 'to' prop
import { Link } from 'react-router-dom'

function Nav() {
  return <Link to="/about">About</Link>
}
```

### Correct: Use next/link with href prop

```tsx
// ✅ GOOD — next/link uses 'href' prop
import Link from 'next/link'

function Nav() {
  return <Link href="/about">About</Link>
}
```

### Wrong: Accessing window directly at module level in a server component

```tsx
// ❌ BAD — This runs on the server during SSR and crashes
// app/analytics/page.tsx (Server Component by default)
const url = window.location.href  // ReferenceError: window is not defined

export default function Analytics() {
  return <p>Current URL: {url}</p>
}
```

### Correct: Use 'use client' and useEffect for browser APIs

```tsx
// ✅ GOOD — Client component with browser API access in useEffect
'use client'

import { useState, useEffect } from 'react'

export default function Analytics() {
  const [url, setUrl] = useState('')

  useEffect(() => {
    setUrl(window.location.href)
  }, [])

  return <p>Current URL: {url}</p>
}
```

### Wrong: Keeping CRA's process.env.REACT_APP_ prefix

```tsx
// ❌ BAD — REACT_APP_ variables are undefined in Next.js
const apiKey = process.env.REACT_APP_API_KEY  // undefined at runtime
```

### Correct: Use NEXT_PUBLIC_ prefix for client-side env vars

```tsx
// ✅ GOOD — NEXT_PUBLIC_ prefix exposes vars to the browser
const apiKey = process.env.NEXT_PUBLIC_API_KEY  // works in both server and client
```

### Wrong: Using CRA image import as a string directly

```tsx
// ❌ BAD — Next.js image imports return an object, not a string
import logo from './logo.png'
<img src={logo} />  // Renders as [object Object]
```

### Correct: Access the .src property or use next/image

```tsx
// ✅ GOOD — Use .src for <img> or the Image component
import logo from './logo.png'
<img src={logo.src} alt="Logo" />

// Even better: Next.js Image with automatic optimization
import Image from 'next/image'
<Image src={logo} alt="Logo" width={200} height={50} />
```

### Wrong: Big-bang migration of all routes at once

```tsx
// ❌ BAD — Removing react-router and converting 50 routes in one PR
// This causes weeks of merge conflicts and blocks feature development
npm uninstall react-router-dom  // Too early!
```

### Correct: Incremental route migration with catch-all fallback

```tsx
// ✅ GOOD — Catch-all route handles unmigrated pages
// app/[[...slug]]/page.tsx keeps react-router working
// Migrate one route at a time:
// 1. Create app/dashboard/page.tsx
// 2. Remove /dashboard from react-router
// 3. Test, ship, repeat
```

### Wrong: Synchronous params access in Next.js 16

```tsx
// ❌ BAD — Synchronous params access removed in Next.js 16
export default function Page({ params }: { params: { id: string } }) {
  return <h1>{params.id}</h1>  // TypeError in Next.js 16
}
```

### Correct: Await params as a Promise in Next.js 16

```tsx
// ✅ GOOD — Async params access required in Next.js 16
export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return <h1>{id}</h1>
}
```

## Common Pitfalls

- **"window is not defined" errors during build**: Components that access `window`, `document`, or `localStorage` crash during server-side rendering. Fix: Add `'use client'` directive and wrap browser API calls in `useEffect`, or use `dynamic(() => import(...), { ssr: false })`. [src1, src2]
- **Global CSS imported outside layout.tsx**: Next.js restricts global CSS imports to `app/layout.tsx` (or layout files). Importing global CSS in page or component files throws a build error. Fix: Move all global CSS imports to `app/layout.tsx` and use CSS Modules for component-scoped styles. [src1]
- **Hydration mismatch between server and client**: If server-rendered HTML differs from client-rendered output (e.g., browser-only content), React throws hydration warnings. Fix: Use `suppressHydrationWarning` on specific elements, or render a loading placeholder server-side and swap in client-side content after mount. [src2]
- **Forgetting to rename environment variables**: All `REACT_APP_*` variables silently become `undefined` in Next.js because it only exposes `NEXT_PUBLIC_*` to the client. Fix: Bulk rename with `sed -i 's/REACT_APP_/NEXT_PUBLIC_/g' .env*` and update all source references. [src1]
- **SVG imports breaking**: CRA auto-converts SVG imports to React components via webpack. Next.js does not do this by default. Fix: Install `@svgr/webpack` and add it to `next.config.ts` webpack config, or use `next/image` for SVGs. [src2]
- **Conflicts with `pages/` directory name**: If your CRA project has a `src/pages/` folder, Next.js may try to treat it as the Pages Router. Fix: Rename to `src/views/` or `src/router-pages/` to avoid collisions. [src2]
- **Static export incompatible with dynamic features**: Using `output: 'export'` disables `useParams`, API routes, proxy (middleware), and other server features. Fix: Remove `output: 'export'` once you need server-side capabilities. [src1]
- **Missing TypeScript types after migration**: The `react-app-env.d.ts` file from CRA provides type declarations for image imports. Fix: Ensure `next-env.d.ts` is in your `tsconfig.json` `include` array: `["next-env.d.ts", "**/*.ts", "**/*.tsx"]`. [src1]
- **Turbopack build failure with custom webpack config (Next.js 16)**: Turbopack is the default in Next.js 16 for both dev and build. Projects with custom `webpack` configs will fail. Fix: Add `--webpack` flag to your build script, or migrate the webpack config to Turbopack-compatible `turbopack.resolveAlias` and loaders. [src5]
- **Sass tilde imports breaking with Turbopack**: Turbopack does not support the legacy `~` prefix for Sass node_modules imports. Fix: Remove the `~` prefix (e.g., `@import '~bootstrap/...'` becomes `@import 'bootstrap/...'`) or use `turbopack.resolveAlias` to map `~*` to `*`. [src5]
- **`revalidateTag()` single-argument call fails to type-check (Next.js 16)**: The single-argument form is deprecated and raises a TypeScript error. Fix: Pass a `cacheLife` profile as the second argument (`revalidateTag('blog-posts', 'max')`), or switch to `updateTag(tag)` inside Server Actions when you need read-your-writes semantics. [src8]
- **`next lint` removed in Next.js 16**: Running `next lint` after upgrading errors out -- `next build` also no longer runs linting. Fix: Run ESLint or Biome directly, or run the codemod `npx @next/codemod@canary next-lint-to-eslint-cli .` to migrate the configuration. [src8]
- **AMP support removed in Next.js 16**: `useAmp`, `export const config = { amp: true }`, and all AMP APIs/configs are gone. Fix: Remove every AMP code path before upgrading -- there is no compatibility shim. [src8]
- **`serverRuntimeConfig` / `publicRuntimeConfig` removed**: Both runtime-config helpers are deleted in Next.js 16. Fix: Move all runtime config to `.env` files and read via `process.env.NEXT_PUBLIC_*` (client) or `process.env.*` (server). [src8]
- **`next.config.ts` Turbopack key moved out of experimental**: `experimental.turbopack` no longer exists -- the config is top-level `turbopack`. Fix: Rename the key after the codemod runs; otherwise startup logs a "config key not recognised" warning and the options are silently ignored. [src8]

## Diagnostic Commands

```bash
# Verify Next.js is installed and working
npx next --version

# Check Node.js version (must be >= 20.9 for Next.js 16)
node --version

# Check for remaining CRA references
grep -rn 'react-scripts' --include='*.json' --include='*.js' --include='*.ts'

# Find remaining REACT_APP_ environment variable usage
grep -rn 'REACT_APP_' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.env*'

# Check for react-router-dom imports (to track migration progress)
grep -rn "from 'react-router" --include='*.tsx' --include='*.ts' --include='*.jsx' | wc -l

# Analyze Next.js build output
npx next build 2>&1 | tail -20

# Check bundle size (Next.js 16 removed size metrics from build output — use Lighthouse)
npx next build && ls -lh .next/static/chunks/

# Verify static export (if using output: 'export')
npx next build && ls build/

# Check for SSR-unsafe window/document access in server components
grep -rn 'window\.\|document\.' --include='*.tsx' --include='*.ts' | grep -v 'node_modules' | grep -v "'use client'"

# Check for synchronous params access (needs async in Next.js 16)
grep -rn 'params\.' --include='page.tsx' --include='layout.tsx' | grep -v 'await'

# Run Next.js codemod for async APIs migration
npx @next/codemod@canary upgrade latest
```

## Version History & Compatibility

| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| Next.js 16.2 (2026-03) | Current | 400% faster dev server startup; 200+ Turbopack fixes; minor caching API refinements | Recommended target for any new CRA migration in 2026; covered by May 2026 security release [src8, src9] |
| Next.js 16.0 (2025-10) | Stable | Async request APIs enforced (params, searchParams, cookies, headers, draftMode are Promises); Turbopack default for dev+build; middleware renamed to proxy; Node.js 18 dropped; TypeScript 5.1 minimum; next/legacy/image deprecated; next lint removed; AMP support removed; serverRuntimeConfig/publicRuntimeConfig removed; revalidateTag requires cacheLife profile arg; experimental.ppr replaced by cacheComponents | Use `npx @next/codemod@canary upgrade latest` for automated migration. `--webpack` flag available for custom configs. [src5, src8] |
| Next.js 15 (2025) | LTS | `params` is now a Promise (must be awaited, but sync still works with warnings); Turbopack stable for dev; fetch/GET no longer cached by default | `const { id } = await params` in page/layout components; safer target for CRA migrations needing maximum library compatibility |
| Next.js 14 (2023) | Maintenance | App Router stable, Server Actions stable | Solid target for CRA migrations; Pages Router still fully supported |
| Next.js 13 (2022) | EOL | App Router introduced (beta), `app/` directory | Pages Router still fully supported alongside App Router |
| CRA 5.x (2021) | Deprecated (Feb 2025) | Last release, no active maintainers | Migrate to Next.js, Vite, or React Router 7 [src3] |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| App needs SSR, SSG, or ISR for SEO/performance | App is a pure client-side dashboard with no SEO needs | Vite + React Router (lighter, simpler) |
| Team wants built-in routing, API routes, and proxy (middleware) | App is tiny (<10 components) and CRA still works | Stay on CRA until it breaks, then move to Vite |
| Need image/font optimization and streaming SSR | Backend team owns API and app is purely a SPA consumer | Vite (faster builds, simpler config) |
| Deploying to Vercel or need edge runtime support | Need full control over server (Express/Fastify) | Remix or custom Vite + Express setup |
| Enterprise app with SEO requirements and complex routing | Building a React Native app | Expo |
| Want React Server Components and the React Compiler | App has complex custom webpack config and no time to migrate | Vite with manual React setup |

## Important Caveats

- Create React App was officially deprecated ("sunset") on February 14, 2025, by the React team. No further updates, security patches, or maintenance will be provided beyond critical fixes. Migration is strongly recommended. [src3]
- The initial migration can keep your app as a pure SPA using `output: 'export'` in `next.config.ts`. Server features (SSR, API routes, proxy) can be adopted incrementally by removing this flag. [src1]
- Next.js 16 defaults to Turbopack for both local development and production builds. If you have custom webpack plugins from CRA/CRACO, you must either use `next dev --webpack` / `next build --webpack` as a fallback, or migrate to Turbopack-compatible options. [src5]
- The `pages/` and `app/` directories can coexist in Next.js, allowing incremental migration from Pages Router to App Router. However, the same route should not exist in both directories. [src6]
- CRA's `public/` folder works similarly in Next.js -- static assets in `public/` are served at the root path. However, `%PUBLIC_URL%` references must be removed (Next.js uses `/` directly). [src1]
- Next.js 16 renamed `middleware.ts` to `proxy.ts` and the `middleware()` export to `proxy()`. The edge runtime is not supported in `proxy` -- it runs on the Node.js runtime. [src5]
- Next.js 16 ships with React 19.2, which includes View Transitions, `useEffectEvent`, and the Activity API. The React Compiler is now stable (opt-in via `reactCompiler: true`). [src5]
- Next.js 16 requires Node.js >= 20.9.0 (LTS). Node.js 18 support was dropped. [src5]
- Next.js 16 introduces **Cache Components** (the new `"use cache"` directive, opt-in via `cacheComponents: true` in `next.config.ts`). Previous `experimental.ppr` and `experimental.dynamicIO` flags have been removed -- their functionality is now part of Cache Components. CRA migrations can ignore this initially: all dynamic code is executed at request time by default. Opt into Cache Components later once the migration is stable. [src8]
- Next.js 16 ships **Next.js DevTools MCP** -- a Model Context Protocol server that gives AI coding agents contextual access to routing, caching, render behavior, browser+server logs, and stack traces during development. Particularly useful for agent-driven CRA-to-Next.js migrations. [src8]
- The **May 2026 Next.js security release** patched 13 CVEs across denial of service, middleware/proxy bypass, SSRF, cache poisoning, and XSS. Both the 16.x and 15.x patch lines received fixes -- pin to the patched version on any branch you ship. [src9]
- New Server Actions caching APIs in Next.js 16: `updateTag(tag)` for **read-your-writes** semantics (form submissions, user settings), `refresh()` for refreshing uncached data without touching the cache. Use these instead of bare `revalidateTag(tag)` in Server Actions. [src8]

## Related Units
<!-- Generated from related_kos frontmatter -->

- [Webpack to Vite Migration](/software/migrations/webpack-to-vite/2026)
- [JavaScript to TypeScript Migration](/software/migrations/javascript-to-typescript/2026)
- [React Class Components to Hooks](/software/migrations/react-classes-to-hooks/2026)
- [jQuery to React Migration](/software/migrations/jquery-to-react/2026)
