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.
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 pemeliharaan
Deprecated (Februari 2025)
Aktif, rilis mayor tiap 6 bulan
Bundler
Webpack 5 (terkunci)
Turbopack (default), Webpack fallback
Routing
react-router-dom (kode)
App Router (file-based)
Rendering
Client-side saja (SPA)
SSR, SSG, ISR, RSC, PPR
Data fetching
useEffect + fetch
Server Components + async/await
Env var prefix
REACT_APP_
NEXT_PUBLIC_ (client-side)
Image handling
<img> manual
next/image dengan AVIF/WebP
Waktu build (aplikasi 50 halaman)
~60–90 detik
~15–25 detik dengan Turbopack
Deployment
Static hosting apapun
Vercel, 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.
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:
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:
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:
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
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:
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:
Menandai layout root sebagai "use client". Ini menghilangkan seluruh manfaat App Router. Turunkan boundary client serendah mungkin.
Pakai NEXT_PUBLIC_ untuk secret. Semua env var berprefix ini akan ter-bundle ke client. Audit dulu sebelum copy-paste dari .env lama.
Tidak menghapus react-router-dom. Library ini masih terinstall dan ikut ter-bundle, menambah ~30 KB gzipped yang percuma. Setelah migrasi routing selesai, uninstall.
Mempertahankan pattern useEffect + fetch untuk data yang sebenarnya bisa di-fetch di server. Ini bikin waterfall dan mengalahkan tujuan migrasi.
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.
Panduan Streaming SSR di Next.js 16 dengan Suspense, loading.tsx, dan Cache Components. Turunkan TTFB dan LCP secara dramatis lewat streaming per-island. Termasuk 8 penyebab streaming gagal dan cara memperbaikinya.
Turbopack jadi default di Next.js 16 tanpa flag apapun. Panduan lengkap konfigurasi rules, filesystem cache, migrasi dari Webpack, dan strategi rollout di monorepo dengan benchmark nyata.
Panduan lengkap error handling di Next.js 16 App Router: error.tsx, global-error.tsx, not-found.tsx, plus fitur baru forbidden() dan unauthorized() dengan contoh kode dan pola retry.