Capítulo 313 de 456

Vite (Migration Guide)

Core Idea

Migração de app Vite pra Next.js segue o mesmo padrão "SPA primeiro" da migração de CRA: index.html vira root layout, main.tsx vira catch-all route com Client Component, e só depois se adota App Router de verdade.

Key Concepts

  • next.config.mjs com output: 'export' + distDir: './dist': mantém output parecido com Vite, sem SSR ainda.
  • tsconfig.json: precisa remover referência a tsconfig.node.json, incluir ./dist/types/**/*.ts + ./next-env.d.ts, excluir node_modules, adicionar plugin { "name": "next" }, e setar esModuleInterop, jsx: "react-jsx", allowJs, forceConsistentCasingInFileNames, incremental todos true.
  • Root layout a partir de index.html: mesmo padrão do CRA, app/layout.tsx recebe <html>/<head>/<body> do index.html do Vite, trocando div#root por <div id="root">{children}</div>.
  • Catch-all route [[...slug]]: page.tsx (Server Component, prerenderizado) importa o CSS global e usa generateStaticParams retornando [{ slug: [''] }]; client.tsx ('use client') usa dynamic(() => import('../../App'), { ssr: false }) pra rodar o app Vite original client-only.
  • Import de imagem muda de shape: Vite retorna string, Next.js retorna objeto; <img src={logo} /> vira <img src={logo.src} />.
  • VITE_NEXT_PUBLIC_: prefixo de env var exposta ao client.
  • Compatibilidade Turbopack com Vite: import.meta.env.MODE/DEV/PROD/BASE_URL/SSR e import.meta.glob funcionam sem mudança; mas queries ?raw/?url do Vite exigem regra customizada em next.config.ts (turbopack.rules).
  • basePath: equivalente ao base URL customizado do Vite.

Code Examples

import '../../index.css'
import { ClientOnly } from './client'

export function generateStaticParams() {
  return [{ slug: [''] }]
}

export default function Page() {
  return <ClientOnly />
}
  • O que demonstra: entrypoint único (Server Component) que delega tudo ao Client Component da app Vite original.
const nextConfig: NextConfig = {
  turbopack: {
    rules: {
      '*': { condition: { query: '?raw' }, type: 'text' },
    },
  },
}
  • O que demonstra: regra Turbopack pra suprir a query ?raw do Vite, que não tem equivalente nativo.

Reference Tables

ViteNext.js
index.htmlapp/layout.tsx
main.tsxapp/[[...slug]]/page.tsx + client.tsx
Import de imagem → stringImport de imagem → objeto (.src)
VITE_*NEXT_PUBLIC_*
import.meta.glob({ as: 'raw' })import.meta.glob({ query: '?raw' }) + regra Turbopack
Base URL customizadobasePath

Anti-patterns

  • Esperar ?raw/?url do Vite funcionar sem config: Turbopack não tem handling nativo, precisa de turbopack.rules no next.config.ts.
  • Manter tsconfig.node.json referenciado: quebra a config do Next.js, precisa remover essa referência.
  • Trocar <img> por <Image> cedo demais sem auto na dimensão não estilizada: mesmo risco de distorção descrito no guia de CRA.

Key Takeaways

  1. Estratégia "SPA primeiro" via catch-all route é idêntica entre migração de CRA e de Vite.
  2. import.meta.env e import.meta.glob do Vite são amplamente compatíveis com Turbopack, exceto queries ?raw/?url.
  3. Depois de estabilizar como SPA, o próximo passo real é abandonar React Router pelo App Router (code splitting automático, streaming, Server Components).

Connects To

  • ch312 Create React App: guia irmão com passos praticamente idênticos.
  • ch311 App Router: destino final da migração incremental depois de estabilizar como SPA.