コンテンツにスキップ

Create React App からの移行

このガイドは、既存の Create React App サイトを Next.js に移行するのに役立ちます。

なぜ移行するのか?

Create React App から Next.js に移行する理由はいくつかあります。

最初のページ読み込み時間が遅い

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

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

自動コード分割がない

前述の読み込み時間の遅さの問題は、コード分割で多少管理できます。ただし、コード分割を手動で行おうとすると、パフォーマンスが悪化することがよくあります。手動でコード分割を行うと、意図せずにネットワークウォーターフォールが発生しやすくなります。Next.js は、ルーターに組み込まれた自動コード分割を提供します。

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

アプリケーションがデータを取得するために、クライアントとサーバー間でリクエストを連続して行う場合、パフォーマンス低下の一般的な原因となります。SPA におけるデータフェッチの一般的なパターンは、最初にプレースホルダーをレンダリングし、コンポーネントがマウントされた後にデータをフェッチすることです。残念ながら、これは、データをフェッチする子コンポーネントが、親コンポーネントが自身のデータのロードを完了するまでフェッチを開始できないことを意味します。

Next.js ではクライアント側でのデータフェッチがサポートされていますが、サーバー側にデータフェッチを移行するオプションも提供しており、クライアントとサーバー間のウォーターフォールを排除できます。

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

React Suspense を通じたストリーミングの組み込みサポートにより、ネットワークウォーターフォールを発生させることなく、UI のどの部分を最初に、どのような順序でロードするかをより意図的に決めることができます。

これにより、ロードが高速なページを構築し、レイアウトシフトを排除できます。

データフェッチ戦略の選択

Next.js では、ニーズに応じて、ページおよびコンポーネントごとにデータフェッチ戦略を選択できます。ビルド時、サーバーでのリクエスト時、またはクライアントでフェッチするかを決定できます。たとえば、CMS からデータをフェッチして、ブログ投稿をビルド時にレンダリングし、それを CDN で効率的にキャッシュできます。

ミドルウェア

Next.js ミドルウェアを使用すると、リクエストが完了する前にサーバー上でコードを実行できます。これは、認証されたユーザーのみがアクセスできるページをユーザーが訪問したときに、認証されていないコンテンツがちらつくのを回避するために、ユーザーをログインページにリダイレクトするのに特に役立ちます。ミドルウェアは、実験や国際化にも役立ちます。

組み込みの最適化

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

移行手順

この移行の目標は、Next.js の機能を段階的に採用できるように、できるだけ早く動作する Next.js アプリケーションを入手することです。まず、既存のルーターを移行せずに、純粋なクライアント側アプリケーション (SPA) として維持します。これにより、移行プロセス中に問題が発生する可能性を最小限に抑え、マージコンフリクトを減らすことができます。

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

最初に、依存関係として next をインストールする必要があります。

ターミナル
npm install next@latest

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

プロジェクトのルートに next.config.mjs を作成します。このファイルには、Next.js の設定オプションが保持されます。

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

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

Next.js のApp Routerアプリケーションには、アプリケーション内のすべてのページをラップするReact Server コンポーネントであるルートレイアウトファイルを含める必要があります。このファイルは、app ディレクトリの最上位レベルで定義されます。

CRA アプリケーションのルートレイアウトファイルに最も近いのは、<html><head><body> タグを含む index.html ファイルです。

このステップでは、index.html ファイルをルートレイアウトファイルに変換します。

  1. src ディレクトリに新しい app ディレクトリを作成します。
  2. その app ディレクトリ内に新しい layout.tsx ファイルを作成します。
app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return '...'
}

覚えておくと便利: レイアウトファイルには、.js.jsx、または .tsx 拡張子を使用できます。

body.div#root および body.noscript タグを <div id="root">{children}</div> に置き換えながら、以前に作成した <RootLayout> コンポーネントに index.html ファイルの内容をコピーします。

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 ファイル、追加のアイコン (ただし、faviconicon、および apple-icon を除く)、およびテスト設定を無視しますが、これらが必要な場合は、Next.js もこれらのオプションをサポートします。詳細については、Metadata API および テストのドキュメントを参照してください。

ステップ 4: メタデータ

Next.js には、デフォルトでmeta charsetおよびmeta viewportタグが既に含まれているため、<head> からこれらのタグを安全に削除できます。

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 は、Metadata 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 のフレームワークに組み込まれた規約ベースのアプローチ (Metadata API) を使用するようになりました。このアプローチにより、SEO を容易に改善し、ページの共有性を高めることができます。

ステップ 5: スタイル

Create React App と同様に、Next.js にはCSS Modulesの組み込みサポートがあります。

グローバル CSS ファイルを使用している場合は、app/layout.tsx ファイルにインポートします。

app/layout.tsx
import '../index.css'
 
// ...

Tailwind を使用している場合は、postcssautoprefixer をインストールする必要があります。

ターミナル
npm install postcss autoprefixer

次に、プロジェクトのルートに postcss.config.js ファイルを作成します。

postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

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

Next.js では、アプリケーションのエントリポイントを `page.tsx` ファイルを作成することで宣言します。CRA でこのファイルに最も近いものは、`src/index.tsx` ファイルです。このステップでは、アプリケーションのエントリポイントを設定します。

`app` ディレクトリに `[[...slug]]` ディレクトリを作成します。

このガイドでは、まず Next.js を SPA(シングルページアプリケーション)として設定することを目指しているため、アプリケーションのすべての可能なルートをキャッチするようにページエントリポイントを設定する必要があります。そのため、`app` ディレクトリに新しい `[[...slug]]` ディレクトリを作成します。

このディレクトリは、オプションのキャッチオールルートセグメントと呼ばれるものです。Next.js はファイルシステムベースのルーターを使用しており、ディレクトリを使用してルートを定義します。この特別なディレクトリにより、アプリケーションのすべてのルートが、そのディレクトリ内の `page.tsx` ファイルにルーティングされるようになります。

`app/[[...slug]]` ディレクトリ内に、次の内容で新しい `page.tsx` ファイルを作成します。

app/[[...slug]]/page.tsx
export function generateStaticParams() {
  return [{ slug: [''] }]
}
 
export default function Page() {
  return '...' // We'll update this
}

このファイルは、サーバーコンポーネントです。`next build` を実行すると、ファイルは静的アセットにプリレンダリングされます。動的なコードは*必要ありません*。

このファイルは、グローバル CSS をインポートし、generateStaticParams に、ルート `/` でインデックスルートを 1 つだけ生成することを伝えます。

次に、クライアントでのみ実行される残りの CRA アプリケーションを移動しましょう。

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'` ディレクティブで定義されたクライアントコンポーネントです。クライアントコンポーネントは、クライアントに送信される前に、サーバー上でHTML にプリレンダリングされます。

クライアントのみのアプリケーションを開始したいので、`App` コンポーネントから下へのプリレンダリングを無効にするように Next.js を構成できます。

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

次に、エントリポイントページを更新して、新しいコンポーネントを使用します。

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

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

Next.js は、CRA とはわずかに異なる方法で静的画像のインポートを処理します。CRA では、画像ファイルをインポートすると、パブリック URL が文字列として返されます。

App.tsx
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` にスタイル設定されていない状態で、1 つの寸法のみがスタイル設定されている画像が含まれている場合、問題が発生する可能性があります。`auto` にスタイル設定されていない場合、寸法は `<img>` の寸法属性の値にデフォルト設定され、画像が歪んで表示される可能性があります。

<img> タグを保持することで、アプリケーションの変更量を減らし、上記の問題を防ぐことができます。その後、ローダーを構成するか、自動画像最適化を備えたデフォルトの Next.js サーバーに移行することで、オプションで <Image> コンポーネントに移行して、画像を最適化する利点を活用できます。

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

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

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

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

または、ファイル名に基づいて画像アセットのパブリック URL を参照できます。たとえば、`public/logo.png` は、アプリケーションの `/logo.png` で画像を配信します。これが `src` の値になります。

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

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

Next.js は、CRA と同様に、`.env` 環境変数をサポートしています。

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

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

これでアプリケーションを実行して、Next.js への移行が成功したかどうかをテストできるはずです。ただし、その前に、`package.json` の `scripts` を 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 で実行されているのを確認できるはずです。

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

これで、Create React App 関連の成果物からコードベースをクリーンアップできます。

  • `public/index.html` を削除します。
  • `src/index.tsx` を削除します。
  • `src/react-app-env.d.ts` を削除します。
  • `reportWebVitals` の設定を削除します。
  • CRA の依存関係 (`react-scripts`) をアンインストールします。

バンドラーの互換性

Create React App と Next.js はどちらも、バンドルに webpack を使用することをデフォルトにしています。

CRA アプリケーションを Next.js に移行する場合、移行を検討しているカスタム webpack 構成があるかもしれません。Next.js は、カスタム webpack 構成の提供をサポートしています。

さらに、Next.js は、ローカル開発のパフォーマンスを向上させるために、`next dev --turbo` を使用した Turbopack をサポートしています。Turbopack は、互換性と段階的な採用のために、一部の webpack ローダーもサポートしています。

次のステップ

すべてが計画どおりに進んだ場合、シングルページアプリケーションとして機能する Next.js アプリケーションが実行されているはずです。ただし、まだ Next.js のメリットのほとんどを活用していませんが、段階的な変更を開始してすべてのメリットを得ることができます。次に実行する必要がある可能性があるものは次のとおりです。

知っておくと良いこと: 静的エクスポートを使用すると、現在 useParams フックの使用をサポートしていません