Migrasi CRA ke Next.js 16: Panduan Langkah Demi Langkah (2026)

Migrasi Create React App ke Next.js 16 App Router: konversi routing react-router-dom, env variables REACT_APP_ ke NEXT_PUBLIC_, data fetching di Server Components, plus checklist dan estimasi waktu dari 12 migrasi nyata.

Diperbarui: 28 Agustus 2026

Migrasi Create React App ke Next.js 16 adalah proses menggantikan react-scripts dengan Next.js App Router, memindahkan konfigurasi Webpack ke Turbopack, mengubah react-router-dom menjadi routing berbasis folder, serta memigrasikan environment variable dari prefix REACT_APP_ ke NEXT_PUBLIC_. Untuk proyek CRA berukuran menengah (30–80 halaman), migrasi biasanya memakan 3–10 hari kerja. Sejak React team resmi menandai Create React App sebagai deprecated pada Februari 2025, Next.js jadi jalur upgrade yang paling langsung dan didokumentasikan penuh oleh tim React.

  • Create React App resmi dihentikan pada Februari 2025 dan tidak lagi menerima update keamanan; migrasi bukan lagi opsional untuk aplikasi produksi.
  • Next.js 16 adalah jalur migrasi yang direkomendasikan React team karena kompatibilitasnya dengan React 19 dan dukungan penuh App Router serta Turbopack.
  • Migrasi minimal (tanpa mengubah pola data fetching) bisa dilakukan dalam 2–4 hari untuk aplikasi kecil; migrasi lengkap ke Server Components memakan 1–3 minggu.
  • Perubahan paling berdampak adalah routing (react-router-dom → App Router), environment variables (REACT_APP_NEXT_PUBLIC_), dan fetching data (useEffect → Server Components).
  • Alternatif selain Next.js: Vite + React Router, Remix (kini React Router v7), dan Astro; masing-masing punya trade-off yang saya bahas di bawah.

Kenapa Create React App dihentikan?

Pada 14 Februari 2025, tim React mengumumkan bahwa Create React App resmi ditandai sebagai deprecated dan tidak lagi direkomendasikan untuk proyek baru. Alasannya sederhana. CRA dibangun sekitar tahun 2016 dengan asumsi bahwa aplikasi React adalah SPA (Single Page Application) murni yang dirender di browser. Model tersebut tidak lagi mencerminkan cara React modern bekerja pada 2026, dengan Server Components, streaming, partial prerendering, dan Suspense-based data fetching.

Masalah teknis yang menumpuk cukup panjang. react-scripts mengunci Webpack 5 dan Babel dengan konfigurasi yang tidak bisa di-eject tanpa membuang seluruh dukungan tooling. Tidak ada dukungan built-in untuk code splitting per route, prefetch cerdas, atau image optimization. Waktu build production untuk aplikasi berukuran menengah bisa mencapai 45–90 detik, dan HMR sering putus setelah beberapa jam mengedit. Yang lebih penting, CRA tidak menerima patch keamanan aktif sejak akhir 2024, dan beberapa CVE di webpack-dev-server dan postcss hanya bisa dimatikan dengan overrides manual di package.json.

Honestly, saya sendiri sudah lama menunda sebuah migrasi klien sampai akhirnya CVE terakhir yang muncul di ci pipeline memaksa saya mengangkat topik ini di rapat sprint. Tim React menyarankan tiga jalur migrasi resmi di pengumuman deprecation resmi mereka: Next.js untuk aplikasi full-stack, React Router (Remix) untuk aplikasi berbasis routing-first, atau Vite untuk SPA yang tidak butuh server rendering. Panduan ini fokus pada jalur pertama karena migrasi ke Next.js memberi Anda paling banyak upside: SSR, ISR, Server Components, dan deployment yang tidak perlu memikirkan konfigurasi.

Perbandingan CRA vs Next.js 16

Sebelum menulis satu baris kode migrasi, penting memahami apa yang benar-benar berubah (dan apa yang tetap sama). Sebagian besar kode komponen Anda (JSX, hooks, state, styling) akan berjalan tanpa modifikasi. Yang berubah adalah cara aplikasi bootstraping, routing, fetching data, dan bundling.

Aspek Create React App Next.js 16 (App Router)
Status pemeliharaanDeprecated (Februari 2025)Aktif, rilis mayor tiap 6 bulan
BundlerWebpack 5 (terkunci)Turbopack (default), Webpack fallback
Routingreact-router-dom (kode)App Router (file-based)
RenderingClient-side saja (SPA)SSR, SSG, ISR, RSC, PPR
Data fetchinguseEffect + fetchServer Components + async/await
Env var prefixREACT_APP_NEXT_PUBLIC_ (client-side)
Image handling<img> manualnext/image dengan AVIF/WebP
Waktu build (aplikasi 50 halaman)~60–90 detik~15–25 detik dengan Turbopack
DeploymentStatic hosting apapunVercel, Netlify, Docker, Node

Yang perlu diperhatikan: Next.js 16 bukan pengganti CRA yang drop-in. Ada perubahan filosofi. App Router mendorong kode default berjalan di server, bukan di client. Untuk sebagian aplikasi CRA yang berat state client-side (misalnya dashboard analitik dengan chart interaktif), Anda mungkin akan menandai banyak komponen dengan "use client". Ini normal dan sesuai desain, tapi perlu diantisipasi.

Persiapan migrasi: checklist sebelum mulai

Sebelum menghapus react-scripts, luangkan waktu setengah hari untuk mendokumentasikan kondisi CRA Anda saat ini. Migrasi yang gagal biasanya karena tim melompat langsung ke kode tanpa memetakan permukaan aplikasi. Berikut checklist yang saya pakai untuk setiap migrasi yang saya bantu:

  • Inventaris routing. Ekspor semua rute dari <Routes>, termasuk nested routes dan dynamic segment. Simpan sebagai tabel dua kolom: path lama → path App Router baru.
  • Audit environment variables. Cari semua process.env.REACT_APP_* di codebase dan kelompokkan berdasarkan apakah value tersebut aman diekspos ke client atau tidak. Yang secret tidak boleh pakai prefix NEXT_PUBLIC_.
  • Identifikasi third-party yang butuh window. Library seperti Leaflet, Chart.js versi lama, atau widget analitik yang mengakses window di initialization phase akan butuh dynamic import dengan ssr: false.
  • Cek versi React. Next.js 16 membutuhkan React 19. Kalau CRA Anda masih di React 17, upgrade React lebih dulu sebelum mulai migrasi supaya breaking change bisa dipisahkan.
  • Snapshot bundle size dan Core Web Vitals. Ambil baseline metrik sekarang supaya setelah migrasi Anda bisa membuktikan (atau menyanggah) klaim performance.
  • Baca panduan migrasi resmi Next.js. Panduan ini dipelihara tim Vercel dan biasanya diperbarui satu minggu setelah rilis mayor.
  • Lihat isu terbuka di repo. Sebelum mulai, saya biasa membuka issue tracker Next.js di GitHub untuk melihat bug apa yang aktif di rilis 16.x. Kadang ada workaround yang bisa menyelamatkan seharian waktu debugging.

Langkah demi langkah migrasi dari CRA ke Next.js 16

Bagian ini adalah playbook konkret. Saya asumsikan Anda sudah membuat branch baru, menyimpan baseline metrik, dan siap eksekusi. Langkah-langkah di bawah ditulis berurutan. Kalau mengeksekusinya dengan urutan lain, build hampir selalu gagal di tengah jalan.

1. Install Next.js dan bersihkan dependensi CRA

Di root proyek CRA yang sama, jalankan:

npm install next@latest react@latest react-dom@latest
npm uninstall react-scripts @craco/craco react-app-rewired

Kemudian ubah scripts di package.json:

{
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  }
}

2. Struktur file dan direktori app/

CRA meletakkan entry point di src/index.js dan src/App.js. Next.js App Router memakai konvensi folder: app/layout.tsx untuk shell root, app/page.tsx untuk halaman utama, dan setiap folder berikutnya menjadi rute. Buat struktur berikut:

app/
  layout.tsx        // menggantikan public/index.html + src/index.js
  page.tsx          // menggantikan route "/"
  globals.css       // pindahkan dari src/index.css
next.config.ts      // konfigurasi bundler
tsconfig.json       // update paths jika perlu

File app/layout.tsx menggantikan public/index.html. Berikut versi minimal:

import type { Metadata } from "next"
import "./globals.css"

export const metadata: Metadata = {
  title: "Aplikasi Saya",
  description: "Migrasi dari CRA",
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="id">
      <body>{children}</body>
    </html>
  )
}

3. Migrasi environment variables

Ubah semua REACT_APP_* menjadi NEXT_PUBLIC_* untuk value yang aman diekspos di browser. Value rahasia (API key backend, database URL, secret) harus dihilangkan prefix publik-nya sehingga hanya bisa diakses di Server Components dan Route Handlers. Contoh .env.local baru:

# Sebelumnya di CRA
# REACT_APP_API_URL=https://api.example.com
# REACT_APP_STRIPE_KEY=pk_live_xxx
# REACT_APP_DATABASE_URL=postgres://... ❌ salah, seharusnya server-only

# Di Next.js 16
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_STRIPE_KEY=pk_live_xxx
DATABASE_URL=postgres://...  # tanpa prefix = server-only

4. Konversi React Router ke App Router

So, ini biasanya bagian yang memakan waktu paling lama. Setiap rute di react-router-dom menjadi folder di dalam app/. Dynamic segment memakai bracket notation. Contoh migrasi:

// Sebelum (react-router-dom)
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/blog" element={<BlogList />} />
  <Route path="/blog/:slug" element={<BlogPost />} />
  <Route path="/dashboard/*" element={<Dashboard />} />
</Routes>

// Sesudah (App Router)
app/
  page.tsx                    → "/"
  blog/
    page.tsx                  → "/blog"
    [slug]/
      page.tsx                → "/blog/:slug"
  dashboard/
    [[...segments]]/
      page.tsx                → "/dashboard/*"

Untuk navigasi programatik, ganti useNavigate() dengan useRouter() dari next/navigation. useParams() dari React Router menjadi useParams() dari next/navigation (nama sama, tapi import berbeda). <Link> di React Router jadi <Link> dari next/link. Prefetch otomatis di Next.js Link membuat navigasi terasa instan tanpa konfigurasi tambahan. Kalau Anda butuh pola routing yang lebih canggih seperti modal berbasis URL, lihat artikel Parallel Routes dan Intercepting Routes di Next.js 16 yang membahas pola-pola yang tidak mungkin dilakukan di CRA.

5. Migrasi data fetching dari useEffect

Di CRA, pola paling umum adalah fetching di useEffect. Ini masih boleh, tetapi Next.js membuka pola yang jauh lebih efisien: fetching langsung di Server Component. Perbandingan singkat:

// CRA / client-side (masih valid)
function BlogList() {
  const [posts, setPosts] = useState([])
  useEffect(() => {
    fetch("/api/posts").then(r => r.json()).then(setPosts)
  }, [])
  return posts.map(p => <article key={p.id}>{p.title}</article>)
}

// Next.js 16 Server Component (lebih cepat, SEO-friendly)
export default async function BlogList() {
  const posts = await fetch("https://api.example.com/posts", {
    next: { revalidate: 60 }
  }).then(r => r.json())
  return posts.map(p => <article key={p.id}>{p.title}</article>)
}

Server Component menghilangkan waterfall client-side dan mengirim HTML yang sudah terisi. Untuk pola caching lanjutan seperti tag-based revalidation dan Partial Prerendering, lihat panduan strategi data fetching dan caching di Next.js 16.

6. Static assets, gambar, dan font

Folder public/ di CRA tetap berfungsi sama persis di Next.js, jadi pindahkan seisinya tanpa perubahan. Yang berubah adalah cara memakai gambar. Ganti <img src="/hero.png" /> dengan <Image> dari next/image:

import Image from "next/image"

<Image
  src="/hero.png"
  alt="Hero"
  width={1200}
  height={600}
  priority
/>

Next.js otomatis akan meng-generate AVIF/WebP, memberi srcset yang tepat, dan menghitung CLS. Detail teknisnya saya bahas di panduan optimasi gambar Next.js 16.

7. Testing setup: Jest ke Vitest (opsional)

CRA memakai Jest yang diintegrasikan react-scripts. Next.js 16 tidak memaksa test framework tertentu, tapi Vitest lebih cepat 2–3× dan integrasinya dengan Turbopack lebih mulus. Kalau Anda memilih tetap dengan Jest, install manual:

npm install --save-dev jest @types/jest jest-environment-jsdom \
  @testing-library/react @testing-library/jest-dom

Kalau pindah ke Vitest:

npm install --save-dev vitest @vitejs/plugin-react jsdom \
  @testing-library/react @testing-library/jest-dom

Sebagian besar test yang ditulis dengan Testing Library akan berjalan tanpa modifikasi (API-nya identik). Yang perlu di-review adalah mock untuk next/router, next/navigation, dan next/image.

8. API layer: dari Express separate ke Route Handlers

Banyak proyek CRA punya backend Express terpisah. Anda tidak wajib menggabungkannya. Tapi kalau backend Anda ringan dan tidak butuh proses long-running, memindahkan ke Route Handlers Next.js menyederhanakan deployment secara drastis. Detail patternnya saya jelaskan di panduan Route Handlers di Next.js 16. Untuk backend berat (queue worker, cron job), lebih aman tetap dipisah.

Menangani Client vs Server Components

Konsep yang paling sering bikin developer CRA bingung adalah pemisahan Client vs Server Components. Di CRA, semua komponen berjalan di browser — titik. Di Next.js 16 App Router, komponen default berjalan di server. Ini sengaja: mengurangi ukuran bundle JavaScript yang dikirim ke browser dan mempercepat first paint.

Aturan praktis yang saya pakai di setiap migrasi:

  • Kalau komponen memakai useState, useEffect, useRef, event handler (onClick, onChange), atau browser API (window, localStorage), tambahkan "use client" di baris pertama.
  • Kalau komponen hanya me-render data (statis atau dari fetch), biarkan sebagai Server Component. Tidak ada boilerplate.
  • Turunkan boundary "use client" serendah mungkin di tree komponen. Menandai layout root sebagai client sama saja dengan menghapus manfaat App Router.
  • Data fetching pindahkan ke Server Component parent, lalu passing hasilnya sebagai props ke Client Component. Ini pattern yang paling scalable.

Untuk library third-party yang menyentuh window pada saat import (misalnya wysiwyg editor lama, chart library certain), pakai dynamic import dengan ssr: false:

import dynamic from "next/dynamic"

const RichEditor = dynamic(() => import("./RichEditor"), {
  ssr: false,
  loading: () => <p>Memuat editor...</p>
})

Estimasi waktu dan effort migrasi

Pertanyaan pertama yang selalu ditanyakan CTO adalah "berapa lama?". Berdasarkan 12 migrasi CRA→Next.js yang saya bantu selama 2024–2026, berikut estimasi realistis. Angka-angka ini asumsinya satu engineer full-time yang sudah kenal React tapi baru pertama kali pakai Next.js App Router:

  • Aplikasi kecil (≤10 halaman, tanpa auth kompleks): 2–4 hari kerja. Migrasi minimal, mempertahankan pattern useEffect data fetching, hanya konversi routing dan env var.
  • Aplikasi menengah (10–50 halaman, ada auth, beberapa third-party): 5–10 hari kerja. Termasuk konversi sebagian data fetching ke Server Components dan setup deployment ulang.
  • Aplikasi besar (50+ halaman, workspace monorepo, custom Webpack config): 3–6 minggu. Umumnya jalan berdampingan dengan CRA lama menggunakan reverse proxy sampai migrasi selesai.
  • Aplikasi dengan SSR sebelumnya (Razzle, custom Express+React): 4–8 hari. Lebih cepat karena tim sudah familiar dengan konsep server rendering.

Aktivitas yang paling sering meleset dari estimasi, dari pengalaman saya: (1) migrasi window-dependent library, (2) menulis ulang custom auth flow yang bergantung pada cookies/session pattern lama, dan (3) memigrasikan testing suite yang punya banyak snapshot test. Untuk auth pattern modern, saya sarankan baca panduan autentikasi Next.js 16 dari proxy.ts hingga Data Access Layer sebelum mulai menulis kode auth baru.

Alternatif selain Next.js: Vite, Remix, Astro

Next.js bukan satu-satunya opsi setelah CRA. Sebagai spesialis framework, saya sering merekomendasikan yang lain kalau kebutuhan Anda tidak cocok. Berikut ringkasan trade-off:

  • Vite + React Router: Pilihan terbaik untuk SPA murni yang tidak butuh SSR. Waktu migrasi paling singkat (1–3 hari) karena mental model paling dekat dengan CRA. Kekurangannya: Anda tetap perlu backend terpisah dan tidak dapat manfaat SEO dari SSR.
  • React Router v7 (dulu Remix): Cocok kalau app Anda routing-heavy dengan banyak nested layout dan loader/action pattern. Lebih ringan dari Next.js tapi ekosistem plugin lebih kecil.
  • Astro: Ideal untuk situs konten (blog, marketing, dokumentasi) yang mostly static dengan sedikit React island. JavaScript yang dikirim ke browser sangat minim.
  • TanStack Start: Framework baru yang matang di 2025–2026. Type-safety end-to-end lewat TanStack Router. Cocok kalau tim Anda sudah pakai TanStack Query intensif.

Jangan pilih framework hanya karena tren. Petakan: apakah Anda butuh SSR/ISR? Berapa halaman? Ada backend eksisting atau tidak? Berapa lama Anda mau invest belajar mental model baru? Jawaban itu yang menentukan, bukan popularitas GitHub.

Kesalahan umum saat migrasi dari CRA

Lima jebakan yang paling sering saya lihat di review migrasi:

  1. Menandai layout root sebagai "use client". Ini menghilangkan seluruh manfaat App Router. Turunkan boundary client serendah mungkin.
  2. Pakai NEXT_PUBLIC_ untuk secret. Semua env var berprefix ini akan ter-bundle ke client. Audit dulu sebelum copy-paste dari .env lama.
  3. Tidak menghapus react-router-dom. Library ini masih terinstall dan ikut ter-bundle, menambah ~30 KB gzipped yang percuma. Setelah migrasi routing selesai, uninstall.
  4. Mempertahankan pattern useEffect + fetch untuk data yang sebenarnya bisa di-fetch di server. Ini bikin waterfall dan mengalahkan tujuan migrasi.
  5. Tidak update tsconfig.json. Path alias dari CRA (@/*) perlu didefinisikan ulang di compilerOptions.paths, dan target module harus bundler.

Pertanyaan yang Sering Diajukan

Apakah Create React App masih bisa dipakai di 2026?

Secara teknis masih bisa. react-scripts tetap tersedia di npm. Tapi CRA tidak lagi menerima update fitur atau patch keamanan sejak Februari 2025. Untuk proyek production, memakainya sama dengan menerima utang teknis yang bertambah setiap bulan.

Apakah migrasi ke Next.js akan mengubah semua kode React saya?

Tidak. Sebagian besar komponen (JSX, hooks, state, styling) akan berjalan tanpa perubahan. Yang berubah hanya lapisan routing, data fetching, environment variables, dan bootstraping. Untuk aplikasi menengah, umumnya 70–85% kode tetap sama persis.

Bisakah saya migrasi bertahap tanpa full rewrite?

Bisa, dengan strategi strangler fig: deploy Next.js paralel dengan CRA lama di subdomain atau path terpisah, lalu migrasi rute per rute. Reverse proxy (Vercel Edge Middleware atau nginx) merutekan trafik ke aplikasi yang tepat. Cara ini efektif untuk aplikasi >50 halaman.

Apa perbedaan utama antara useEffect fetching dan Server Components?

useEffect fetching terjadi di browser setelah komponen mount, membuat waterfall dan menampilkan loading state. Server Component fetching terjadi di server saat request tiba, sehingga HTML yang dikirim sudah lengkap dengan data, jadi lebih cepat untuk pengguna dan lebih baik untuk SEO.

Perlukah saya pakai TypeScript saat migrasi?

Tidak wajib, tapi sangat direkomendasikan. Next.js 16 punya dukungan TypeScript first-class dan sebagian besar contoh dokumentasi resmi ditulis dalam TypeScript. Kalau CRA Anda masih JavaScript murni, tambahkan TypeScript di PR terpisah setelah migrasi routing selesai. Jangan campur dua migrasi besar sekaligus.

Jasmine Patel
Tentang Penulis Jasmine Patel

Web framework specialist comparing Next.js to everything else so you don't have to. Migrates teams off legacy stacks for fun.