Capítulo 312 de 456

Create React App (Migration Guide)

Core Idea

Migração incremental de CRA pra Next.js: primeiro vira uma SPA rodando em cima do App Router (catch-all route + Client Component), depois adota features server-side aos poucos.

Key Concepts

  • Motivação: CRA é 100% client-side rendering, sem code splitting automático, com network waterfalls comuns; Next.js resolve isso com fetch no servidor, streaming (Suspense) e code splitting automático.
  • output: 'export' + distDir: 'build': config inicial em next.config.ts pra gerar SPA estática, mantendo estrutura de output parecida com CRA (sem SSR/API por enquanto).
  • Root layout a partir de public/index.html: app/layout.tsx recebe o conteúdo de <html>/<head>/<body> do index.html, substituindo body div#root por <div id="root">{children}</div>.
  • Metadata automática: Next.js já injeta charset/viewport; favicon/ícones colocados no topo de app são detectados automaticamente (Metadata API), dispensando <link> manual.
  • Catch-all route [[...slug]]: app/[[...slug]]/page.tsx com generateStaticParams retornando [{ slug: [''] }] intercepta todas as rotas numa única página, preservando o roteador client-side existente (React Router) durante a migração.
  • Client-only entrypoint: app/[[...slug]]/client.tsx com 'use client' + dynamic(() => import('../../App'), { ssr: false }) embala o App root do CRA como Client Component sem SSR.
  • Static image imports mudam de shape: CRA retorna string (URL); Next.js retorna objeto ({ src, width, height }), então <img src={logo} /> vira <img src={logo.src} /> (ou usar <Image> diretamente).
  • REACT_APP_NEXT_PUBLIC_: prefixo de env var exposta ao client muda de nome.
  • Turbopack por padrão: next dev usa Turbopack; next dev --webpack reproduz o bundler do CRA se precisar de config webpack customizada.
  • basePath: substitui o campo homepage do package.json do CRA pra servir sob subpath.
  • rewrites(): substitui o campo proxy do CRA pra encaminhar requests de API pro backend.

Code Examples

'use client'
import dynamic from 'next/dynamic'

const App = dynamic(() => import('../../App'), { ssr: false })

export function ClientOnly() {
  return <App />
}
  • O que demonstra: embrulho client-only do App root do CRA, sem SSR, preservando comportamento SPA original.
const nextConfig: NextConfig = {
  output: 'export', // Outputs a Single-Page Application (SPA)
  distDir: 'build', // Changes the build output directory to `build`
}
  • O que demonstra: config inicial que replica o comportamento estático do CRA (sem SSR ainda).

Reference Tables

CRANext.js
public/index.htmlapp/layout.tsx
src/index.tsx (entrypoint)app/[[...slug]]/page.tsx + client.tsx
Import de imagem → stringImport de imagem → objeto (.src)
REACT_APP_*NEXT_PUBLIC_*
homepage no package.jsonbasePath no next.config.ts
proxy no package.jsonrewrites() no next.config.ts
react-scriptsnext dev/next build/next start

Anti-patterns

  • Migrar o roteador (React Router) junto com o resto: a estratégia recomendada trata a app como SPA primeiro (catch-all route), migrando pro App Router de fato só depois, incrementalmente.
  • Usar <Image> direto sem ajustar dimensões: componente seta width/height automaticamente; se só uma dimensão for estilizada (sem auto na outra), a imagem pode distorcer. Manter <img> com .src até migrar com calma.
  • Esquecer next-env.d.ts no tsconfig.json: gera erro de tipo ao acessar .src de imagem importada.

Key Takeaways

  1. A estratégia oficial é "SPA primeiro" via catch-all route, minimizando conflitos e permitindo adoção incremental depois.
  2. Import de imagem muda de string (CRA) pra objeto (Next.js): sempre acessar .src se mantiver <img>.
  3. output: 'export' remove acesso a features server-side (SSR, API Routes) até ser removido do config.
  4. Prefixo de env var pública muda de REACT_APP_ pra NEXT_PUBLIC_.

Connects To

  • ch311 App Router: próximo passo após estabilizar a SPA é migrar de fato pro modelo de rotas/data fetching do App Router.
  • ch313 Vite: guia irmão com passos quase idênticos pra quem vem de Vite em vez de CRA.