Create React Appからの移行
このガイドは、既存のCreate React App (CRA) サイトをNext.jsに移行するのに役立ちます。
なぜ切り替えるのか?
Create React AppからNext.jsに切り替えたい理由はいくつかあります。
初回ページ読み込み時間の遅さ
Create React Appは純粋にクライアントサイドのReactを使用しています。クライアントサイドのみのアプリケーション、別名シングルページアプリケーション (SPA) は、初回ページ読み込み時間が遅いという問題が発生することがよくあります。これはいくつかの理由によります。
- ブラウザがReactコードとアプリケーションバンドル全体をダウンロードして実行し、そのコードがデータをロードするためのリクエストを送信できるようになるまで待機する必要があります。
- 追加する新しい機能や依存関係が増えるごとに、アプリケーションコードが肥大化します。
自動コード分割がない
前述の読み込み時間の遅さの問題は、コード分割によってある程度軽減できます。しかし、手動でコード分割を行おうとすると、意図せずネットワークウォーターフォールを引き起こす可能性があります。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の設定オプションが格納されます。
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アプリケーションにおけるルートレイアウトファイルに最も近いものは、<html>
、<head>
、<body>
タグを含むpublic/index.html
です。
src
ディレクトリ内に(またはルートにapp
を置く場合はプロジェクトルートに)新しいapp
ディレクトリを作成します。app
ディレクトリ内に、layout.tsx
(またはlayout.js
)ファイルを作成します。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}
古いindex.html
の内容をこの<RootLayout>
コンポーネントにコピーします。body div#root
(およびbody noscript
)を<div id="root">{children}</div>
に置き換えます。
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とTestingセットアップでサポートされています。
ステップ4: メタデータ
Next.jsは<meta charset="UTF-8" />
と<meta name="viewport" content="width=device-width, initial-scale=1" />
タグを自動的に含めるため、<head>
からこれらを削除できます。
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.ico
、icon.png
、robots.txt
などのメタデータファイルは、app
ディレクトリのトップレベルに配置されている限り、アプリケーションの<head>
タグに自動的に追加されます。サポートされているすべてのファイルをapp
ディレクトリに移動した後、それらの<link>
タグは安全に削除できます。
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
オブジェクトに移動してください。
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
にインポートしてください。
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として維持し、すべてのルートをインターセプトしたいため、オプションのキャッチオールルートを使用します。
app
内に[[...slug]]
ディレクトリを作成します。
app
┣ [[...slug]]
┃ ┗ page.tsx
┣ layout.tsx
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
)を作成します。
'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
)を更新して新しいコンポーネントを使用するようにします。
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>
タグを維持することで、アプリケーションの変更量を減らし、上記の問題を防ぐことができます。その後、オプションで<Image>
コンポーネントに移行し、ローダーを設定して画像を最適化したり、自動画像最適化機能を備えたデフォルトのNext.jsサーバーに移行したりすることができます。
/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でアプリケーションを実行するとこのファイルを自動的に生成します。
ステップ9: 環境変数を移行する
Next.jsはCRAと同様に環境変数をサポートしていますが、ブラウザで公開したい変数はNEXT_PUBLIC_
というプレフィックスを必要とします。
主な違いは、クライアントサイドで環境変数を公開するために使用するプレフィックスです。REACT_APP_
プレフィックスを持つすべての環境変数をNEXT_PUBLIC_
に変更してください。
ステップ10: package.json
のスクリプトを更新する
package.json
のスクリプトをNext.jsコマンドを使用するように更新します。また、.next
とnext-env.d.ts
を.gitignore
に追加します。
{
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "npx serve@latest ./build"
}
}
# ...
.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.json
でhomepage
フィールドを使って特定のサブパスでアプリを提供していた場合、Next.jsではnext.config.ts
のbasePath
設定を使ってこれを再現できます。
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
basePath: '/my-subpath',
// ...
}
export default nextConfig
カスタムService Worker
の処理
CRAのサービスワーカー(例: create-react-app
のserviceWorker.js
)を使用していた場合は、Next.jsでプログレッシブウェブアプリケーション (PWA)を作成する方法を学ぶことができます。
APIリクエストのプロキシ
CRAアプリでpackage.json
のproxy
フィールドを使ってバックエンドサーバーにリクエストを転送していた場合、Next.jsではnext.config.ts
のNext.js rewritesでこれを再現できます。
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.jsの設定を拡張できます。
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
webpack: (config, { isServer }) => {
// Modify the webpack config here
return config
},
}
export default nextConfig
注: これには、
dev
スクリプトから--turbopack
を削除してTurbopackを無効にする必要があります。
TypeScriptの設定
Next.jsはtsconfig.json
があればTypeScriptを自動的にセットアップします。tsconfig.json
のinclude
配列に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の機能を活用していませんが、これらを段階的に導入していくことができます。
- React RouterからNext.js App Routerへの移行により、
- 自動コード分割
- ストリーミングサーバーレンダリング
- Reactサーバーコンポーネント
<Image>
コンポーネントで画像を最適化するnext/font
でフォントを最適化する<Script>
コンポーネントでサードパーティスクリプトを最適化するnpx next lint
を実行し、プロジェクトのニーズに合わせて設定することで、Next.js推奨ルールでESLintを有効にする
注: 静的エクスポート (
output: 'export'
) は現在、useParams
フックやその他のサーバー機能をサポートしていません。すべてのNext.js機能を使用するには、next.config.ts
からoutput: 'export'
を削除してください。
役に立ちましたか?