blue red and green letters illustration

For years, create-react-app (CRA) was the undisputed king. It was the npx create-react-app my-app that launched a thousand projects. It gave us a modern, client-side-rendered (CSR) React setup with zero configuration. But the web evolved. The need for better SEO, faster initial page loads, and more integrated full-stack experiences grew. CRA, being purely client-side, started to show its age.

Enter Next.js, the full-stack React framework. And more recently, its new paradigm: the App Router.

Migrating from CRA to the Next.js App Router isn't just a "find and replace." It's a fundamental shift from a client-centric application to a server-centric one. This shift unlocks powerful features like Server-Side Rendering (SSR), Static Site Generation (SSG), and, most importantly, React Server Components (RSCs). The result is a faster, more powerful, and more SEO-friendly application.

Here’s a high-level guide to making the switch.


1. Setup and Folder Structure

You'll start by scaffolding a new Next.js project: npx create-next-app@latest. Choose "Yes" for using the app/ directory.

The folder structure is the first major change:

  • CRA: src/pages/, src/components/, public/index.html

  • Next.js (App Router): app/, public/ (no index.html), and you can keep a components/ folder.

Your App.js and index.css from CRA will conceptually merge into app/layout.jsx and app/globals.css. The app/layout.jsx is your new root, and it must define the <html> and <body> tags.


2. The New File-Based Routing

This is the biggest change. CRA used react-router-dom to define routes in a central file. The App Router uses a directory-based system.

CRA (react-router)

Next.js (App Router)

<Route path="/" element={<Home />}>

app/page.jsx

<Route path="/about" element={<About />}>

app/about/page.jsx

<Route path="/blog/:id" element={<Post />}>

app/blog/[id]/page.jsx

layout.jsx with <Outlet />

app/dashboard/layout.jsx (wraps routes in dashboard/)

To migrate, you will:

  1. Identify your "pages" in CRA.

  2. For each page, create a new folder in the app/ directory.

  3. Inside that folder, move your page component's content into a new file named page.jsx.

  4. Replace all react-router-dom's <Link> components with <Link> from next/link.


3. The Data Fetching Paradigm Shift

In CRA, you fetched data on the client inside a useEffect:

JavaScript

// CRA (Client-Side)
useEffect(() => {
  fetch('/api/posts')
    .then(res => res.json())
    .then(data => setPosts(data));
}, []);

This is slow. The user gets a loading spinner while they wait for the JavaScript to load, then wait for the data to be fetched.

With the App Router, components are Server Components by default. This means they run on the server. You can fetch data directly inside them using async/await.

JavaScript

// Next.js (Server Component)
async function getPosts() {
  const res = await fetch('https://api.example.com/posts');
  return res.json();
}

export default async function BlogPage() {
  const posts = await getPosts(); // This happens on the server!

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

The user's browser receives the fully-rendered HTML with the data already included. There is no loading state. It's dramatically faster and perfect for SEO.


4. "use client": The Client-Side Opt-In

"Wait," you say, "what about useState or onClick? They don't work on the server!"

You are correct. This is the most important concept: Server Components cannot use hooks or interactivity.

When you need a component to be interactive (use state, effects, or event listeners), you must explicitly mark it as a Client Component by placing the "use client" directive at the very top of the file.

JavaScript

// app/components/Counter.jsx
"use client"; // This is now a Client Component

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

Your app/blog/page.jsx (Server Component) can then import and render this Counter (Client Component). This is the "hybrid" model: fetch data on the server, and send interactive "islands" of JavaScript to the client.

The migration is an investment. You're not just moving files; you're re-architecting your application to be faster, more efficient, and more powerful.

VISITOR NO. :

VISITOR NO :

5218

6:07:15 AM

Create a free website with Framer, the website builder loved by startups, designers and agencies.