コンテンツにスキップ

Create React Appからの移行

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

なぜ切り替えるのか?

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

初回ページ読み込み時間の遅さ

Create React Appは純粋にクライアントサイドのReactを使用します。クライアントサイドのみのアプリケーションは、シングルページアプリケーション (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.json のカスタム homepage フィールド、カスタムサービスワーカー、特定の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サーバーコンポーネントです。

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のMetadata 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は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とウェブ上での共有可能性をより簡単に向上させることができます。

ステップ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は空のスラッグ(/)に対して単一のルートを生成し、事実上すべてのルートを同じページにマッピングします。このページはサーバーコンポーネントであり、静的HTMLにプリレンダリングされます。

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

次に、CRAのルートAppコンポーネントをクライアントコンポーネント内に埋め込み、すべてのロジックがクライアントサイドに残るようにします。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' ディレクティブにより、このファイルはクライアントコンポーネントになります。
  • 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>コンポーネントと直接使用できるか、または既存の<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でアプリケーションを実行すると、このファイルを自動的に生成します。

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

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

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

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

Next.jsコマンドを使用するように package.json スクリプトを更新します。また、.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

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.jsではnext.config.tsbasePath設定を使用してそれを再現できます。

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.jsではnext.config.tsリライト機能を使用してこれを再現できます。

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.jsの設定をnext.config.tsで拡張できます。

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

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

TypeScriptセットアップ

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

{
  "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' を削除してください。