コンテンツにスキップ
ガイド移行Create React App

Create React App から Next.js への移行方法

このガイドでは、既存の Create React App (CRA) サイトを Next.js に移行する方法を説明します。

なぜ切り替えるのか?

Create React App から Next.js に切り替えたい理由はいくつかあります。

初期ページ読み込み時間の遅延

Create React App は純粋にクライアントサイドレンダリングを使用します。クライアントサイド専用アプリケーション、またはシングルページアプリケーション (SPA) としても知られるものは、初期ページの読み込み時間が遅くなることがよくあります。これはいくつかの理由で発生します。

  1. ブラウザは、React コードとアプリケーション全体のバンドルがダウンロードされて実行されるのを待つ必要があります。その後、コードはデータをロードするためのリクエストを送信できるようになります。
  2. アプリケーションコードは、新しい機能や依存関係を追加するたびに増大します。

自動コード分割がない

以前の遅い読み込み時間の問題は、コード分割である程度緩和できます。ただし、手動でコード分割を試みると、誤ってネットワークウォーターフォールを導入する可能性があります。Next.js は、ルーターとビルドパイプラインに自動コード分割とツリーシェイキングを組み込んでいます。

ネットワークウォーターフォール

パフォーマンスの低下の一般的な原因は、アプリケーションがデータを取得するために逐次的なクライアントサーバーリクエストを行うことです。SPA でのデータ取得の 1 つのパターンは、プレースホルダーをレンダリングしてから、コンポーネントがマウントされた後にデータを取得することです。残念ながら、子コンポーネントは、親コンポーネントが独自のデータのロードを完了した後にしかデータの取得を開始できないため、「ウォーターフォール」のリクエストが発生します。

Next.js ではクライアントサイドのデータ取得がサポートされていますが、データ取得をサーバーに移動することもできます。これにより、クライアントサーバーのウォーターフォールが完全に解消されることがよくあります。

高速で意図的なローディング状態

React Suspense によるストリーミングの組み込みサポートにより、ネットワークウォーターフォールを作成することなく、UI のどの部分が最初にロードされ、どの順序でロードされるかを定義できます。

これにより、読み込みの速いページを構築し、レイアウトシフト を排除することができます。

データ取得戦略を選択する

ニーズに応じて、Next.js ではページまたはコンポーネントレベルでデータ取得戦略を選択できます。たとえば、CMS からデータを取得してビルド時にブログ投稿をレンダリング (SSG) して高速な読み込み速度を実現したり、必要に応じてリクエスト時にデータを取得 (SSR) したりできます。

プロキシ

Next.js プロキシを使用すると、リクエストが完了する前にサーバーでコードを実行できます。たとえば、認証済みのページにユーザーをリダイレクトすることで、認証されていないコンテンツのフラッシュを回避できます。A/B テスト、実験、国際化などの機能にも使用できます。

組み込みの最適化

画像フォントサードパーティスクリプトは、アプリケーションのパフォーマンスに大きな影響を与えることがよくあります。Next.js には、これらを自動的に最適化する特殊なコンポーネントと API が含まれています。

移行手順

私たちの目標は、できるだけ早く動作する Next.js アプリケーションを取得することです。これにより、Next.js の機能を段階的に採用できるようになります。まず、既存のルーターをすぐに置き換えることなく、アプリケーションを純粋なクライアントサイドアプリケーション (SPA) として扱います。これにより、複雑さとマージコンフリクトが軽減されます。

注意: `package.json` のカスタム `homepage` フィールド、カスタムサービスワーカー、または特定の Babel/webpack の調整など、高度な CRA 設定を使用している場合は、このガイドの最後にある追加の考慮事項セクションを参照して、Next.js でこれらの機能を複製または適応する方法を確認してください。

ステップ 1: Next.js の依存関係をインストールする

既存のプロジェクトに Next.js をインストールする

ターミナル
npm install next@latest

ステップ 2: Next.js 設定ファイルを作成する

`package.json` と同じレベルのプロジェクトのルートに `next.config.ts` を作成します。このファイルには、Next.js の設定オプションが含まれています。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  output: 'export', // Outputs a Single-Page Application (SPA)
  distDir: 'build', // Changes the build output directory to `build`
}
 
export default nextConfig

注意: `output: 'export'` を使用することは、静的エクスポートを実行することを意味します。SSR や API などのサーバーサイド機能にはアクセスできません。Next.js のサーバー機能を利用するには、この行を削除してください。

ステップ 3: ルートレイアウトの作成

Next.js のApp Router アプリケーションには、すべてのページをラップするルートレイアウトファイル (React Server Component) を含める必要があります。

CRA アプリケーションでルートレイアウトファイルに最も近いものは `public/index.html` で、``, ``, `` タグが含まれています。

  1. `src` フォルダー内 (または `app` をルートにしたい場合はプロジェクトルート) に新しい `app` ディレクトリを作成します。
  2. `app` ディレクトリ内に `layout.tsx` (`layout.js`) ファイルを作成します。
app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return '...'
}

次に、古い `index.html` の内容をこの `` コンポーネントにコピーします。`body div#root` (および `body noscript`) を `

`id="root"`{children}`
` に置き換えます。

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <meta charSet="UTF-8" />
        <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>React App</title>
        <meta name="description" content="Web site created..." />
      </head>
      <body>
        <div id="root">{children}</div>
      </body>
    </html>
  )
}

知っておくと良いこと: Next.js は、デフォルトで CRA の `public/manifest.json`、追加のアイコン、およびテスト設定を無視します。これらが必要な場合は、Next.js はメタデータ API およびテストセットアップでサポートしています。

ステップ 4: メタデータ

Next.js は、`` と `` タグを自動的に含めるため、`` から削除できます。

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
        <title>React App</title>
        <meta name="description" content="Web site created..." />
      </head>
      <body>
        <div id="root">{children}</div>
      </body>
    </html>
  )
}

favicon.icoicon.pngrobots.txt などの メタデータファイルは、app ディレクトリの最上位に配置されている限り、アプリケーションの <head> タグに自動的に追加されます。サポートされているすべてのファイルapp ディレクトリに移動した後、それらの <link> タグを安全に削除できます。

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <title>React App</title>
        <meta name="description" content="Web site created..." />
      </head>
      <body>
        <div id="root">{children}</div>
      </body>
    </html>
  )
}

最後に、Next.js は メタデータ API を使用して、最後の <head> タグを管理できます。最終的なメタデータ情報をエクスポートされた metadata オブジェクトに移動します。

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'React App',
  description: 'Web site created with Next.js.',
}
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <div id="root">{children}</div>
      </body>
    </html>
  )
}

上記変更により、index.html ですべてを宣言するのではなく、フレームワークに組み込まれた Next.js の規約ベースのアプローチ(メタデータ API)を使用するようになりました。このアプローチにより、ページの SEO と Web での共有可能性をより簡単に向上させることができます。

ステップ 5: スタイル

CRA と同様に、Next.js はCSS Modules を標準でサポートしています。また、グローバル CSS のインポートもサポートしています。

グローバル CSS ファイルがある場合は、`app/layout.tsx` にインポートします。

app/layout.tsx
import '../index.css'
 
export const metadata = {
  title: 'React App',
  description: 'Web site created with Next.js.',
}
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <div id="root">{children}</div>
      </body>
    </html>
  )
}

Tailwind CSS を使用している場合は、インストール ドキュメントを参照してください。

ステップ 6: エントリーポイントページの作成

Create React App は `src/index.tsx` (`index.js`) をエントリーポイントとして使用します。Next.js (App Router) では、`app` ディレクトリ内の各フォルダがルートに対応し、各フォルダには `page.tsx` が必要です。

現時点ではアプリを SPA として維持し、すべてのルートをインターセプトしたいので、オプショナルキャッチオールルートを使用します。

  1. `app` ディレクトリ内に `[[...slug]]` ディレクトリを作成します。
app
  [[...slug]]
   page.tsx
  layout.tsx
  1. page.tsx に以下を追加します。:
app/[[...slug]]/page.tsx
export function generateStaticParams() {
  return [{ slug: [''] }]
}
 
export default function Page() {
  return '...' // We'll update this
}

これにより、Next.js は空のスラッグ (`/`) に対して 1 つのルートを生成し、すべてのルートを同じページにマッピングします。このページはサーバーコンポーネントであり、静的 HTML に事前レンダリングされます。

ステップ 7: クライアント専用エントリーポイントの追加

次に、CRA のルート App コンポーネントをクライアントコンポーネントに埋め込み、すべてのロジックをクライアントサイドに保ちます。Next.js を初めて使用する場合は、クライアントコンポーネント (デフォルトでは) がサーバーで事前レンダリングされることを知っておくと役立ちます。これらは、クライアントサイド JavaScript を実行できる追加機能を持っていると考えることができます。

`app/[[...slug]]/` に `client.tsx` (`client.js`) を作成します。

app/[[...slug]]/client.tsx
'use client'
 
import dynamic from 'next/dynamic'
 
const App = dynamic(() => import('../../App'), { ssr: false })
 
export function ClientOnly() {
  return <App />
}
  • `'use client'` ディレクティブにより、このファイルはクライアントコンポーネントになります。
  • `ssr: false` を指定した `dynamic` インポートは、`` コンポーネントのサーバーサイドレンダリングを無効にし、真にクライアント専用 (SPA) にします。

次に、`page.tsx` (`page.js`) を更新して、新しいコンポーネントを使用します。

app/[[...slug]]/page.tsx
import { ClientOnly } from './client'
 
export function generateStaticParams() {
  return [{ slug: [''] }]
}
 
export default function Page() {
  return <ClientOnly />
}

ステップ 8: 静的画像インポートの更新

CRA では、画像ファイルをインポートすると、そのパブリック URL が文字列として返されます。

import image from './img.png'
 
export default function App() {
  return <img src={image} />
}

Next.js では、静的画像インポートはオブジェクトを返します。このオブジェクトは、Next.js の <Image> コンポーネントで直接使用できます。または、オブジェクトの src プロパティを既存の <img> タグで使用することもできます。

<Image> コンポーネントには、自動画像最適化の利点が追加されています。<Image> コンポーネントは、画像の次元に基づいて、結果の <img>width および height 属性を自動的に設定します。これにより、画像が読み込まれる際のレイアウトシフトを防ぐことができます。ただし、アプリに片方の次元のみがスタイリングされており、もう片方の次元が auto にスタイリングされていない画像が含まれている場合、問題が発生する可能性があります。auto にスタイリングされていない場合、次元は <img> の次元属性の値にデフォルト設定され、画像が歪んで表示される可能性があります。

<img> タグを保持することで、アプリケーションでの変更量を減らし、上記の問題を防ぐことができます。その後、ローダーを設定するか、デフォルトの Next.js サーバーに移行して画像を最適化するために、オプションで後で <Image> コンポーネントに移行できます。デフォルトの Next.js サーバーには自動画像最適化機能が組み込まれています。

/public からインポートした画像の絶対インポートパスを相対インポートに変換します。

// Before
import logo from '/logo.png'
 
// After
import logo from '../public/logo.png'

<img> タグに画像オブジェクト全体ではなく、画像 src プロパティを渡します。

// Before
<img src={logo} />
 
// After
<img src={logo.src} />

または、ファイル名に基づいて画像アセットの公開 URL を参照することもできます。たとえば、public/logo.png は、アプリケーションの /logo.png で画像を提供するため、これが src 値になります。

警告: TypeScript を使用している場合、`src` プロパティにアクセスする際に型エラーが発生する可能性があります。これらのエラーを解決するには、`tsconfig.json` ファイルの `include` 配列 に `next-env.d.ts` を追加する必要があります。Next.js は、ステップ 9 でアプリケーションを実行すると、このファイルを自動的に生成します。

ステップ 9: 環境変数の移行

Next.js は CRA と同様に環境変数をサポートしていますが、ブラウザで公開したい変数はすべて `NEXT_PUBLIC_` プレフィックスを必須とします。

主な違いは、クライアントサイドで環境変数を公開するために使用されるプレフィックスです。`REACT_APP_` プレフィックスを持つすべての環境変数を `NEXT_PUBLIC_` に変更してください。

ステップ 10: `package.json` のスクリプトの更新

package.json のスクリプトを Next.js コマンドを使用するように更新します。また、`.next` および `next-env.d.ts` を `.gitignore` に追加します。

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "npx serve@latest ./build"
  }
}
.gitignore
# ...
.next
next-env.d.ts

これで実行できます。

npm run dev

https://:3000 を開きます。アプリケーションが Next.js (SPA モード) で実行されているのが表示されるはずです。

ステップ 11: クリーンアップ

これで、Create React App 固有のアーティファクトを削除できます。

  • public/index.html
  • src/index.tsx
  • src/react-app-env.d.ts
  • `reportWebVitals` のセットアップ
  • `react-scripts` 依存関係 (package.json からアンインストールします)。

追加の考慮事項

CRA でカスタム `homepage` を使用する

CRA `package.json` の `homepage` フィールドを使用してアプリを特定のサブパスで提供していた場合、`next.config.ts` の`basePath` 設定を使用して Next.js でこれを再現できます。

next.config.ts
import { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  basePath: '/my-subpath',
  // ...
}
 
export default nextConfig

カスタム `Service Worker` の処理

CRA のサービスワーカー (例: `create-react-app` からの `serviceWorker.js`) を使用していた場合、Next.js でプログレッシブウェブアプリケーション (PWA) を作成する方法を学べます。

API リクエストのプロキシ

CRA アプリで `package.json` の `proxy` フィールドを使用してバックエンドサーバーにリクエストを転送していた場合、`next.config.ts` のNext.js リライトでこれを再現できます。

next.config.ts
import { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'https://your-backend.com/:path*',
      },
    ]
  },
}

カスタム Webpack

CRA でカスタム webpack または Babel 設定を使用していた場合、`next.config.ts` で Next.js の設定を拡張できます。

next.config.ts
import { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  webpack: (config, { isServer }) => {
    // Modify the webpack config here
    return config
  },
}
 
export default nextConfig

注意: これには、`dev` スクリプトに `--webpack` を追加して Webpack を使用する必要があります。

TypeScript のセットアップ

Next.js は、`tsconfig.json` が存在する場合、TypeScript を自動的にセットアップします。`tsconfig.json` の `include` 配列に `next-env.d.ts` がリストされていることを確認してください。

{
  "include": ["next-env.d.ts", "app/**/*", "src/**/*"]
}

バンドラーの互換性

Create React App はバンドリングに webpack を使用します。Next.js は現在、ローカル開発を高速化するために Turbopack をデフォルトで使用しています。

next dev  # Uses Turbopack by default

代わりに Webpack を使用するには (CRA と同様)

next dev --webpack

CRA から高度な webpack 設定を移行する必要がある場合は、`next.config.ts` で引き続きカスタム webpack 設定を提供できます。

次のステップ

すべてがうまくいった場合、シングルページアプリケーションとして実行される動作する Next.js アプリケーションが完成しました。まだサーバーサイドレンダリングやファイルベースルーティングなどの Next.js 機能を利用していませんが、段階的にそれらを利用できるようになりました。

  • React Router からNext.js App Router への移行
  • 画像の最適化 (`` コンポーネントを使用)
  • フォントの最適化 (`next/font` を使用)
  • サードパーティスクリプトの最適化 (`