コンテンツへスキップ

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 サーバーコンポーネントであり、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拡張子を使用できます。

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

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 でもこれらのオプションがサポートされています。詳細については、メタデータ APIおよびテストに関するドキュメントを参照してください。

ステップ 4: メタデータ

Next.js には、デフォルトで、メタ charsetおよびメタビューポートタグがすでに含まれているため、<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 は メタデータ 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: スタイル

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にスタイル設定せずに、片方のサイズのみがスタイル設定されている画像が含まれている場合、問題が発生する可能性があります。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配列tsconfig.jsonファイルにnext-env.d.tsを追加する必要があります。Next.jsは、ステップ9でアプリケーションを実行すると、このファイルを自動的に生成します。

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

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

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

ステップ9:package.jsonのスクリプトを更新する

Next.jsに正常に移行できたかどうかをテストするために、アプリケーションを実行できるようになりました。しかし、その前に、package.jsonscriptsをNext.js関連のコマンドで更新し、.nextnext-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フックの使用はサポートされていません