メインコンテンツまでスキップ

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 Middlewareを使用すると、リクエストが完了する前にサーバーでコードを実行できます。これは、認証されたページにユーザーがアクセスしたときに未認証のコンテンツが一瞬表示されるのを避けるために、ユーザーをログインページにリダイレクトするのに特に役立ちます。ミドルウェアは、実験や国際化にも役立ちます。

組み込みの最適化

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

移行手順

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

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

最初に行うべきことは、nextを依存関係としてインストールすることです:

Terminal
npm install next@latest

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

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

next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // シングルページアプリケーション(SPA)を出力します。
distDir: './build', // ビルド出力ディレクトリを`./build`に変更します。
}

export default nextConfig

ステップ3: Root レイアウトを作成する

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

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

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

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

Good to know: レイアウトファイルには.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>
)
}

Good to know: Next.jsはCRAのpublic/manifest.jsonファイル、追加のアイコン(faviconicon、およびapple-iconを除く)、およびテスト設定を無視しますが、これらが必要な場合、Next.jsもこれらのオプションをサポートしています。詳細については、メタデータ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はメタデータ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とページのウェブ共有性をより簡単に向上させることができます。

ステップ5: スタイル

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

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

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

// ...

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

Terminal
npm install postcss autoprefixer

次に、プロジェクトのrootに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 '...' // これを更新します
}

このファイルはServer Componentです。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 />
}

このファイルはClient Componentであり、'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>コンポーネントで直接使用することができるか、既存の<img>タグでオブジェクトのsrcプロパティを使用することができます。

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

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

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

// 以前
import logo from '/logo.png'

// 以後
import logo from '../public/logo.png'

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

// 以前
<img src={logo} />

// 以後
<img src={logo.src} />

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

警告: TypeScriptを使用している場合、srcプロパティにアクセスするときに型エラーが発生する可能性があります。それを修正するには、tsconfig.jsonファイルのinclude配列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を実行し、http://localhost: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 --turbopackを通じてTurbopackをサポートしており、ローカル開発のパフォーマンスを向上させます。Turbopackは、互換性と段階的な採用のために一部のwebpackローダーもサポートしています。

次のステップ

すべてが計画通りに進んだ場合、現在、シングルページアプリケーションとして動作するNext.jsアプリケーションがあります。ただし、まだNext.jsの利点の多くを活用していませんが、段階的に変更を加えてすべての利点を享受することができます。次に行うことを検討すること:

Good to know: 静的エクスポートを使用する場合、useParamsフックの使用は現在サポートされていません