Create React App からの移行
このガイドは、既存のCreate React AppサイトをNext.jsに移行する手助けをします。
どうして切り替えるの?
Create React AppからNext.jsに切り替える理由はいくつかあります:
初回ページ読み込み時間が遅い
Create React Appは純粋にクライアントサイドのReactを使用しています。クライアントサイドのみのアプリケーション、またはシングルページアプリケーション(SPA)として知られるものは、初回ページ読み込み時間が遅くなることがよくあります。これにはいくつかの理由があります:
- ブラウザがReactのコードやアプリケーションバンドル全体のダウンロードと実行を待たなければならないため、コードがデータをロードするリクエストを送信できるようになる前です。
- 新しい機能や依存関係を追加することでアプリケーションコードが増えていきます。
自動コード分割がない
前述の遅い読み込み時間の問題は、コード分割である程度管理できます。しかし、手動でコード分割を行おうとすると、パフォーマンスが悪化することがあります。手動でコード分割を行うと、不意にネットワークのウォーターフォールを導入することが容易です。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 の設定オプションが含まれます。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // シングルページアプリケーション (SPA) を出力します。
distDir: './build', // ビルド出力ディレクトリを './dist'' に変更します。
}
export default nextConfig
ステップ 3: Root Layout の作成
Next.jsのApp Routerアプリケーションは、アプリケーション内のすべてのページをラップするReact Server ComponentのRoot レイアウトファイルを含める必要があります。このファイルは app
ディレクトリのトップレベルに定義されます。
CRAアプリケーションでのRoot レイアウトファイルに最も近いものは、<html>
、<head>
、および<body>
タグを含むindex.html
ファイルです。
このステップでは、index.html
ファイルをRoot レイアウトファイルに変換します:
src
ディレクトリに新しいapp
ディレクトリを作成します。- その
app
ディレクトリ内にlayout.tsx
ファイルを作成します:
- TypeScript
- JavaScript
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}
export default function RootLayout({ children }) {
return '...'
}
Good to know: レイアウトファイルには
.js
、.jsx
、または.tsx
拡張子を使用できます。
index.html
ファイルの内容を前に作成した<RootLayout>
コンポーネントにコピーし、body.div#root
とbody.noscript
タグを<div id="root">{children}</div>
に置き換えます:
- TypeScript
- JavaScript
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>
)
}
export default function RootLayout({ children }) {
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
ファイル、追加のアイコン画像(一部のfavicon
、icon
、apple-icon
を除く)やテスト設定は無視されますが、これらが必要な場合、Next.jsもオプションとしてこれらをサポートしています。メタデータ APIやテストドキュメントを参照してください。
ステップ 4: メタデータ
Next.jsはデフォルトで meta charset および meta viewport タグを既に含んでいるため、これらを<head>
から削除しても問題ありません:
- TypeScript
- JavaScript
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>
)
}
export default function RootLayout({ children }) {
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.ico
、icon.png
、robots.txt
などのメタデータファイルは、アプリケーションの <head>
タグに自動的に追加されます。これらのメタデータファイルは、app
ディレクトリのトップレベルに配置するだけで使用できます。次にすべてのサポートされているファイルを app
ディレクトリに移動した後、<link>
タグを削除しても問題ありません:
- TypeScript
- JavaScript
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>
)
}
export default function RootLayout({ children }) {
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
オブジェクトに移動します:
- TypeScript
- JavaScript
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>
)
}
export const metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({ children }) {
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
ファイルにインポートします:
- TypeScript
import '../index.css'
// ...
Tailwindを使用している場合、postcss
およびautoprefixer
をインストールする必要があります:
npm install postcss autoprefixer
次に、プロジェクトのルートに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
ファイルを作成
- TypeScript
- JavaScript
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
このファイルは Server Componentです。next build
を実行すると、このファイルは静的アセットとしてプリレンダリングされます。動的なコードは必要ではありません。
このファイルはグローバルなCSSをインポートし、generateStaticParams
を使って1つのルート、/
というインデックスルートのみを生成することを指示します。
次に、Next.jsのクライアントのみのアプリケーションとなるため、CRAアプリケーションの残りの部分を移動しましょう。
- TypeScript
- JavaScript
'use client'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
'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 })
次に、エントリポイントページを更新して、新しいコンポーネントを使用します:
- TypeScript
- JavaScript
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
ステップ 7: 静的画像インポートの更新
Next.jsは、CRAとは少し異なる方法で静的画像のインポートを処理します。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.ts
をtsconfig.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
を追加する必要があります:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "npx serve@latest ./build"
}
}
# ...
.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の多くの利点を活用していませんが、段階的に変更を行い、すべての利点を享受することができます。次に行うべきことがあるとすれば:
- React Routerから Next.js App Router に移行する:
- 自動コード分割
- ストリーミングサーバーレンダリング
- React サーバー コンポーネント
<Image>
コンポーネントを使用して画像を最適化するnext/font
を使用してフォントを最適化する<Script>
コンポーネントを使用してサードパーティスクリプトを最適化する- Next.jsのルールをサポートするようにESLint設定を更新する
Good to know: 静的エクスポートを使用することは、現在
useParams
フックを使用することをサポートしていません。