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

Create React Appからの移行

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

なぜ切り替えるのか?

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

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

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

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

自動コード分割がない

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

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

パフォーマンスが悪化する一般的な原因は、アプリケーションがデータを取得するためにクライアントとサーバー間で順次リクエストを行うことです。SPAでのデータ取得のパターンの1つは、プレースホルダーをレンダリングし、コンポーネントがマウントされた後にデータを取得することです。残念ながら、子コンポーネントは親が自身のデータを読み込むのを完了した後にのみデータを取得し始めることができ、リクエストの「ウォーターフォール」を引き起こします。

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

高速で意図的な読み込み状態

React Suspenseを通じたストリーミングの組み込みサポートにより、ネットワークウォーターフォールを作成せずに、UIのどの部分を最初にどの順序で読み込むかを定義できます。

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

データ取得戦略の選択

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

ミドルウェア

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

組み込みの最適化

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

移行手順

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

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

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

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

Terminal
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', // シングルページアプリケーション(SPA)を出力します
distDir: 'build', // ビルド出力ディレクトリを`build`に変更します
}

export default nextConfig

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

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

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

CRAアプリケーションでのroot レイアウトファイルに最も近いものは、<html><head>、および<body>タグを含む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の内容をこの<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、追加のアイコン、およびテスト設定をデフォルトで無視します。これらが必要な場合、Next.jsはMetadata APITestingのセットアップでサポートしています。

ステップ4: メタデータ

Next.jsは<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />タグを自動的に含めるため、<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とWebの共有性をより簡単に向上させることができます。

ステップ5: スタイル

CRAと同様に、Next.jsはCSSモジュールを標準でサポートしています。また、グローバル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として保持し、すべてのルートをインターセプトしたいので、optional catch-all routeを使用します。

  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 '...' // 後で更新します
}

これはNext.jsに、空のスラッグ(/)に対して単一のルートを生成するように指示し、すべてのルートを同じページにマッピングします。このページはServer Componentであり、静的HTMLにプリレンダリングされます。

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

次に、CRAのroot AppコンポーネントをClient Component内に埋め込み、すべてのロジックをクライアントサイドに保持します。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'ディレクティブは、このファイルをClient Componentにします。
  • dynamicインポートとssr: falseは、<App />コンポーネントのサーバーサイドレンダリングを無効にし、純粋にクライアント専用(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>コンポーネントで直接使用することができ、または既存の<img>タグでオブジェクトのsrcプロパティを使用することができます。

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

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

/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でアプリケーションを実行するときにこのファイルを自動的に生成します。

ステップ9: 環境変数を移行する

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

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

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

package.jsonのスクリプトをNext.jsのコマンドに更新します。また、.nextnext-env.d.ts.gitignoreに追加します。

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

次のコマンドを実行できます。

npm run dev

http://localhost: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.jsonhomepageフィールドを使用してアプリを特定のサブパスで提供していた場合、Next.jsのbasePath設定を使用してnext.config.tsでそれを再現できます。

next.config.ts
import { NextConfig } from 'next'

const nextConfig: NextConfig = {
basePath: '/my-subpath',
// ...
}

export default nextConfig

カスタムService Workerの処理

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

APIリクエストのプロキシ

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

next.config.ts
import { NextConfig } from 'next'

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

カスタムWebpack / Babel設定

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

next.config.ts
import { NextConfig } from 'next'

const nextConfig: NextConfig = {
webpack: (config, { isServer }) => {
// ここでwebpack設定を変更します
return config
},
}

export default nextConfig

注意: これには、devスクリプトから--turbopackを削除してTurbopackを無効にする必要があります。

TypeScriptのセットアップ

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

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

バンドラーの互換性

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

next dev --turbopack

CRAから高度なwebpack設定を移行する必要がある場合は、カスタムwebpack設定を提供することもできます。

次のステップ

すべてが正常に動作した場合、現在、シングルページアプリケーションとして動作するNext.jsアプリケーションがあります。まだNext.jsのサーバーサイドレンダリングやファイルベースのルーティングなどの機能を活用していませんが、段階的に行うことができます。

注意: 静的エクスポート(output: 'export')を使用すると、useParamsフックや他のサーバー機能が現在サポートされていません。Next.jsのすべての機能を使用するには、next.config.tsからoutput: 'export'を削除してください。