コンテンツにスキップ
ガイド移行Create React App

Create React AppからNext.jsへの移行方法

このガイドでは、既存のCreate React App(CRA)サイトをNext.jsに移行する方法を説明します。

なぜ切り替えるのか?

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

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

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

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

自動コード分割がない

以前の遅い読み込み時間問題は、コード分割である程度緩和できます。しかし、手動でコード分割を試みると、意図せずにネットワークウォーターフォールを導入する可能性があります。Next.jsは、ルーターとビルドパイプラインに組み込まれた自動コード分割とツリーシェイキングを提供します。

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

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

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

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

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

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

データ取得戦略を選択する

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

プロキシ

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

組み込みの最適化

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

移行手順

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

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

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

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

ターミナル
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', // Outputs a Single-Page Application (SPA)
  distDir: 'build', // Changes the build output directory to `build`
}
 
export default nextConfig

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

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

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

CRAアプリケーションにおけるルートレイアウトファイルに最も近いものはpublic/index.htmlであり、<html><head><body>タグが含まれています。

  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>
  )
}

ご存知でしたか:Next.jsは、デフォルトでCRAのpublic/manifest.json、追加のアイコン、およびテスト構成を無視します。これらが必要な場合は、Next.jsはメタデータAPIおよびテストセットアップでサポートしています。

ステップ 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 は メタデータ 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:スタイル

CRAと同様に、Next.jsはCSS Modulesをすぐにサポートします。また、グローバル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として維持し、すべてのルートをインターセプトしたいので、オプショナルキャッチオールルートを使用します。

  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 '...' // We'll update this
}

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

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

次に、CRAのルート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になります。
  • ssr: falseを指定したdynamicインポートは、<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> コンポーネントで直接使用できます。または、オブジェクトの src プロパティを既存の <img> タグで使用することもできます。

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

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

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

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

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

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

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

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

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

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

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

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

package.jsonスクリプトを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(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.config.tsbasePath構成を使用してNext.jsでこれを再現できます。

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でProgressive Web Applications(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

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

next.config.ts
import { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  webpack: (config, { isServer }) => {
    // Modify the webpack config here
    return config
  },
}
 
export default nextConfig

注意:これには、devスクリプトに--webpackを追加してWebpackを使用する必要があります。

TypeScriptのセットアップ

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

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

バンドラーの互換性

Create React Appはバンドルにwebpackを使用します。Next.jsは現在、高速なローカル開発のためにTurbopackをデフォルトで使用しています。

next dev  # Uses Turbopack by default

Webpackを使用するには(CRAと同様に)

next dev --webpack

CRAから高度なwebpack設定を移行する必要がある場合は、next.config.tsカスタムwebpack構成を引き続き提供できます。

次のステップ

すべてがうまくいけば、シングルページアプリケーションとして動作する機能的なNext.jsアプリケーションが完成しました。まだサーバーサイドレンダリングやファイルベースルーティングなどのNext.js機能を利用していませんが、段階的にそれらを利用できるようになります。

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