Cra to Nextjs
Migrate a Create React App project to Next.js for server-side rendering and file-based routing
You are an expert in migrating Create React App (CRA) projects to Next.js for server-side rendering, static generation, file-based routing, and improved performance. ## Key Points 1. **Install Next.js** — add Next.js alongside the existing CRA setup. 2. **Restructure to App or Pages Directory** — move components into the Next.js file-based routing structure. 3. **Replace React Router** — convert route definitions to file-system routes. 4. **Migrate Data Fetching** — move API calls from `useEffect` to `getServerSideProps`, `getStaticProps`, or Server Components. 5. **Update Configuration** — translate CRA environment variables and proxy setup to `next.config.js`. 6. **Remove CRA** — uninstall `react-scripts` and related dependencies. - Migrate pages one at a time. Keep CRA running in parallel until all routes are moved. - Mark components that use `useState`, `useEffect`, or browser APIs with `'use client'` at the top of the file. - Use Server Components by default; only add `'use client'` when interactivity is required. - Replace `react-helmet` or `react-helmet-async` with Next.js `metadata` exports. - Move API calls out of `useEffect` into server-side fetching wherever possible to eliminate loading spinners and improve SEO. - Use `next/image` to replace `<img>` tags for automatic optimization. ## Quick Example ```bash npm install next npm uninstall react-scripts ```
skilldb get migration-patterns-skills/Cra to NextjsFull skill: 256 linesCreate React App to Next.js — Migration Patterns
You are an expert in migrating Create React App (CRA) projects to Next.js for server-side rendering, static generation, file-based routing, and improved performance.
Core Philosophy
Overview
CRA is a client-side-only React setup with no built-in server rendering or routing conventions. Next.js provides SSR, SSG, API routes, file-based routing, and image optimization out of the box. The migration involves restructuring files into Next.js conventions, replacing React Router with file-based routing, and adapting data fetching to use Next.js patterns.
Migration Strategy
- Install Next.js — add Next.js alongside the existing CRA setup.
- Restructure to App or Pages Directory — move components into the Next.js file-based routing structure.
- Replace React Router — convert route definitions to file-system routes.
- Migrate Data Fetching — move API calls from
useEffecttogetServerSideProps,getStaticProps, or Server Components. - Update Configuration — translate CRA environment variables and proxy setup to
next.config.js. - Remove CRA — uninstall
react-scriptsand related dependencies.
Step-by-Step Guide
1. Install Next.js
npm install next
npm uninstall react-scripts
2. Restructure the project
CRA structure:
src/
App.js
index.js
pages/
Home.js
About.js
UserProfile.js
components/
Header.js
Footer.js
Next.js App Router structure:
app/
layout.tsx
page.tsx ← Home
about/
page.tsx ← About
users/
[id]/
page.tsx ← UserProfile
src/
components/
Header.tsx
Footer.tsx
3. Create the root layout (replaces index.js + App.js shell)
// app/layout.tsx
import { Header } from '@/components/Header';
import { Footer } from '@/components/Footer';
import '@/styles/globals.css';
export const metadata = {
title: 'My App',
description: 'Migrated from CRA to Next.js',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
4. Replace React Router with file-based routing
// Before — CRA with React Router
// src/App.js
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import UserProfile from './pages/UserProfile';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
</BrowserRouter>
);
}
// After — Next.js file-based routing
// app/page.tsx
export default function HomePage() {
return <h1>Welcome</h1>;
}
// app/about/page.tsx
export default function AboutPage() {
return <h1>About Us</h1>;
}
// app/users/[id]/page.tsx
export default function UserProfilePage({ params }: { params: { id: string } }) {
return <h1>User {params.id}</h1>;
}
5. Replace navigation
// Before — React Router
import { useNavigate, Link } from 'react-router-dom';
const navigate = useNavigate();
navigate('/about');
<Link to="/users/42">View Profile</Link>
// After — Next.js
import { useRouter } from 'next/navigation';
import Link from 'next/link';
const router = useRouter();
router.push('/about');
<Link href="/users/42">View Profile</Link>
6. Migrate data fetching to Server Components
// Before — CRA useEffect pattern
function UserProfile() {
const { id } = useParams();
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${id}`).then(r => r.json()).then(setUser);
}, [id]);
if (!user) return <Spinner />;
return <div>{user.name}</div>;
}
// After — Next.js Server Component (no loading spinner needed)
// app/users/[id]/page.tsx
async function getUser(id: string) {
const res = await fetch(`${process.env.API_URL}/users/${id}`, { cache: 'no-store' });
if (!res.ok) throw new Error('Failed to fetch user');
return res.json();
}
export default async function UserProfilePage({ params }: { params: { id: string } }) {
const user = await getUser(params.id);
return <div>{user.name}</div>;
}
7. Update environment variables
# CRA uses REACT_APP_ prefix
REACT_APP_API_URL=https://api.example.com
# Next.js uses NEXT_PUBLIC_ prefix for client-side variables
NEXT_PUBLIC_API_URL=https://api.example.com
# Server-only variables need no prefix in Next.js
API_SECRET=secret_key
8. Update next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Proxy API requests (replaces CRA's proxy field in package.json)
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://localhost:8080/api/:path*',
},
];
},
images: {
domains: ['cdn.example.com'],
},
};
module.exports = nextConfig;
9. Update package.json scripts
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}
Best Practices
- Migrate pages one at a time. Keep CRA running in parallel until all routes are moved.
- Mark components that use
useState,useEffect, or browser APIs with'use client'at the top of the file. - Use Server Components by default; only add
'use client'when interactivity is required. - Replace
react-helmetorreact-helmet-asyncwith Next.jsmetadataexports. - Move API calls out of
useEffectinto server-side fetching wherever possible to eliminate loading spinners and improve SEO. - Use
next/imageto replace<img>tags for automatic optimization.
Common Pitfalls
- Forgetting
'use client'— components using hooks, event handlers, or browser APIs must be marked as Client Components. - Window/document references — code that accesses
windowordocumentat the top level breaks during SSR. Guard withtypeof window !== 'undefined'or move touseEffect. - CSS-in-JS compatibility — some CSS-in-JS libraries (styled-components, emotion) require additional configuration for SSR. Check Next.js docs for your library.
- Direct
process.envaccess on the client — onlyNEXT_PUBLIC_*variables are exposed to the browser. Server-only env vars are undefined on the client. - Losing SPA behavior — Next.js client-side navigation via
<Link>is fast, but using<a>tags causes full page reloads. Always usenext/link.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add migration-patterns-skills
Related Skills
Class to Functional React
Convert React class components to functional components with hooks
Javascript to Typescript
Migrate a JavaScript codebase to TypeScript incrementally with minimal disruption
Jest to Vitest
Migrate a test suite from Jest to Vitest for faster execution and native ESM support
Monolith to Microservices
Decompose a monolithic application into microservices using the strangler fig pattern
REST to GRAPHQL
Migrate a REST API to GraphQL while maintaining backward compatibility
SQL to Nosql
Migrate from a relational SQL database to a NoSQL document or key-value store