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

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を依存関係としてインストールします:

ターミナル
npm install next@latest

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

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

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

export default nextConfig

ステップ 3: Root Layout の作成

Next.jsのApp Routerアプリケーションは、アプリケーション内のすべてのページをラップするReact Server ComponentRoot レイアウトファイルを含める必要があります。このファイルは 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#rootbody.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ファイル、追加のアイコン画像(一部のfaviconiconapple-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などのメタデータファイルは、アプリケーションの <head> タグに自動的に追加されます。これらのメタデータファイルは、app ディレクトリのトップレベルに配置するだけで使用できます。次にすべてのサポートされているファイル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 Modulesのサポートが組み込まれています。

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

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

// ...

Tailwindを使用している場合、postcssおよびautoprefixerをインストールする必要があります:

ターミナル
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
}

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

このファイルはグローバルなCSSをインポートし、generateStaticParamsを使って1つのルート、/というインデックスルートのみを生成することを指示します。

次に、Next.jsのクライアントのみのアプリケーションとなるため、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'ディレクティブで定義されています。Client Componentsは、クライアントに送信される前にサーバーで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 からインポートされた画像の絶対インポートパスを相対インポートに変換:

// 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 プロパティにアクセスするときに型エラーが発生する可能性があります。これを修正するには、next-env.d.tstsconfig.json ファイルの include 配列 に追加する必要があります。Next.jsは、ステップ9でアプリケーションを実行するときにこのファイルを自動的に生成します。

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

Next.jsには、CRAと同様に .env 環境変数のサポートが備わっています。

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

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

これでNext.jsに正常に移行できたかどうかをテストするためにアプリケーションを実行できるはずです。ただし、その前に、package.jsonのスクリプトをNext.js関連のコマンドで更新し、.gitignoreファイルに .next および next-env.d.ts を追加する必要があります:

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構成](/docs/app/api-reference/config/next-config-js/webpack)の提供をサポートしています。

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

次のステップ

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

Good to know: 静的エクスポートを使用することは、現在useParamsフックを使用することをサポートしていません