Migrating from Create React App
このガイドは、既存の Create React App サイトを Next.js に移行するのに役立ちます。
Why Switch?
いくつかの理由から Create React App から Next.js に切り替えたいと思うかもしれません:
Slow initial page loading time
Create React App は純粋に Client サイドの React を使用します。Client サイドのみのアプリケーション、つまりシングルページアプリケーション(SPAs)は、初期ページの loading 時間が遅いという問題をしばしば経験します。これはいくつかの理由から起こります:
- ブラウザは、React code とあなたのアプリケーションバンドル全体がダウンロードされて実行されるのを待つ必要があり、その後で初めてあなたの code がデータをロードするための Request を送信できます。
- あなたのアプリケーションの code は、新たな機能や依存性を追加するたびに増えていきます。
No automatic code splitting
前回の loading 時間の遅さの問題は、 code の分割で多少対凇することが可能です。しかし、 code の分割を手動で行うと、パフォーマンスが悪化することがよくあります。手動でコードを分割すると、うっかりネットワークの滝のような状態を引き起こすことがあります。 Next.js はその router に組み込まれた automatic code splitting を提供します。
Network waterfalls
アプリケーションが fetch 用のデータを client サーバに直列に requests すると、パフォーマンスが低下する一般的な原因が発生します。SPA でのデータの取得における一般的なパターンは、まず placeholder を render し、その後 component がマウントされた後に fetch データをすることです。残念ながら、これはデータを fetch する子 component が、親 component が自身のデータの loading を終えるまで start できないことを意味します。
Next.js を使用すると、 client 上でのデータ取得がサポートされるだけでなく、データ取得を server にシフトするオプションも提供します。これにより、クライアント-サーバー間のウォーターフォールを排除することができます。
Fast and intentional loading states
React Suspense を通じたストリーミングの組み込みサポートを利用することで、ネットワークの滝を引き起こすことなく、どの UI の部分を最初に、そしてどの順序でロードしたいかをより意図的に制御できます。
これにより、ページのロードが速くなり、レイアウトシフト を排除することができます。
Choose the data fetching strategy
あなたのニーズに応じて、 Next.js はページと component ごとにデータ取得の strategy を選択することができます。あなたは build 時、 server 上での request 時、または client 上でデータを fetch することを決定することができます。例えば、あなたの CMS からデータを fetch し、ブログの投稿を build 時に render することができます。これにより、CDN 上で効率的にキャッシュすることができます。
Middleware
Next.js Middleware は、リクエストが完了する前にサーバー上でコードを実行することを可能にします。これは、ユーザーが認証が必要なページを訪れた際に認証されていないコンテンツのちらつきを避けるために、ユーザーをログインページにリダイレクトする場合などに特に役立ちます。また、ミドルウェアは実験や国際化にも役立ちます。
Built-in Optimizations
Images、フォント、およびサードパーティのスクリプトは、アプリケーションのパフォーマンスに大きな影響を与えることがよくあります。Next.js には、これらを自動的に最適化する組み込みコンポーネントが含まれています。
Migration Steps
このマイグレーションの目標は、できるだけ早く動作する Next.js アプリケーションを取得することで、その後、 Next.js の機能を段階的に採用することができます。まず、既存の router をマイグレーションせずに、純粋なクライアントサイドアプリケーション(SPA)として保持します。これにより、マイグレーションプロセス中に問題が発生する可能性を最小限に抑え、マージコンフリクトを減らすことができます。
Step 1: Install the Next.js Dependency
最初に行うべきことは、依存関係として next
をインストールすることです:
npm install next@latest
Step 2: Create the Next.js Configuration File
プロジェクトの root にnext.config.mjs
を作成します。このファイルには、あなたのNext.js 設定 optionsが保存されます。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // Outputs a Single-Page Application (SPA).
distDir: './dist', // Changes the build output directory to `./dist/`.
}
export default nextConfig
Good to know: Next.js の設定ファイルには、
.js
または.mjs
のいずれかを使用できます。
Step 3: Update TypeScript Configuration
あなたが TypeScript を使用している場合、それを Next.js と互換性を持たせるためには、次の変更をあなたの tsconfig.json
ファイルに更新する必要があります:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"./dist/types/**/*.ts"
],
"exclude": ["node_modules"]
}
TypeScript の設定に関する詳細情報は、Next.js のドキュメントで見つけることができます。
Step 4: Create the Root Layout
Next.js App Router アプリケーションには、アプリケーション内のすべてのページを包括する React Server Component である root layout ファイルが必要です。このファイルは app
ディレクトリの最上位に定義されます。
CRA アプリケーションにおける root layout ファイルの最も近い同等物は、index.html
ファイルで、これは<html>
、<head>
、そして<body>
タグを含んでいます。
このステップでは、index.html
ファイルを root layout ファイルに変換します:
src
ディレクトリ内に新しいapp
ディレクトリを作成します。- その
app
ディレクトリ内に新しいlayout.tsx
ファイルを作成します:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return null
}
export default function RootLayout({ children }) {
return null
}
Good to know: Layout ファイルには
.js
,.jsx
,.tsx
の拡張子が使用できます。
- 作成済みの
<RootLayout>
component にindex.html
ファイルの内容をコピーし、body.div#root
およびbody.script
タグを<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>
)
}
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: マニフェストファイル、 favicon 以外の追加アイコノグラフィー、テスト構成を無視しますが、これらが要求である場合、 Next.js はこれらの options もサポートします。
- Next.js は default でmeta charset とmeta viewport タグを含んでいるため、
<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>
)
}
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
などのメタデータファイルは、それらが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>
)
}
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 は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>
)
}
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 の慣例に基づくアプローチ(Metadata API)を使用するように移行しました。このアプローチにより、ページの SEO とウェブ共有性をより簡単に向上させることができます。
Step 5: Create the Entrypoint Page
Next.js では、page.tsx
ファイルを作成することでアプリケーションのエントリーポイントを宣言します。このファイルの最も近い同等物は CRA 上のsrc/index.tsx
ファイルです。このステップでは、アプリケーションのエントリーポイントを設定します。
app
ディレクトリに[[...slug]]
ディレクトリを作成します。
このガイドは、まず Next.js を SPA(Single Page Application)として設定することを目指しているので、アプリケーションのすべての可能な routes を catch all するためのページエントリーポイントが必要です。それを実現するために、app
ディレクトリ内に新しい [[...slug]]
ディレクトリを作成してください。
このディレクトリは、optional catch-all route segmentと呼ばれるものです。Next.js はファイルシステムに基づいたルーターを使用し、ディレクトリを使用してルートを定義します。この特別なディレクトリは、アプリケーションのすべてのルートが含まれるpage.tsx
ファイルに向けられることを保証します。
app/[[...slug]]
ディレクトリ内に新しいpage.tsx
ファイルを以下の内容で作成します:
import '../../src/index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
import '../../src/index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
Good to know:
.js
,.jsx
, または.tsx
拡張子はページファイルに使用できます。
このファイルはServer Componentです。next build
を実行すると、このファイルは静的アセットにプレレンダリングされます。動的なコードは必要ありません。
このファイルはグローバル CSS をインポートし、generateStaticParams
に対して、/
でインデックスルートのみを生成することを伝えます。
さあ、CRA アプリケーションの残りを移動しましょう。これは Client だけで実行されます。
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../src/App'), { ssr: false })
export function ClientOnly() {
return <App />
}
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../src/App'), { ssr: false })
export function ClientOnly() {
return <App />
}
このファイルは 'use client'
ディレクティブによって定義されたClient Componentです。Client Components はクライアントに送信される前にサーバー上でHTML へプレレンダリングされます。
Client 専用アプリケーションを最初から始めたい場合、App
component からプレレンダリングを無効にするように Next.js を設定できます。
const App = dynamic(() => import("../../App"), { ssr: false });
Now, update your entrypoint page to use the new component:
import '../../src/index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
import '../../src/index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
Step 6: Update Static Image Imports
Next.js は、CRA とは若干異なる方法で静的な image のインポートを処理します。CRA では、 image ファイルをインポートすると、その public URL が string として返されます:
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>
タグを保持することで、アプリケーションの変更を少なくし、上記の問題を防ぐことができます。その後、ローダーを設定することにより画像を最適化するために<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
プロパティにアクセスするときに型エラーが発生することがあります。 これは当面無視して構いません。このガイドの最後に修正されます。
Step 7: Migrate the Environment Variables
Next.js は、CRA と同様に、.env
environment variablesをサポートしています。
主な違いは、クライアント側で environment variables を公開するために使用される接頭辞です。REACT_APP_
接頭辞を使用しているすべての environment variables をNEXT_PUBLIC_
に変更してください。
Step 8: Update Scripts in package.json
これで Next.js への移行が成功したかどうかをテストするためにアプリケーションを実行できるようになりました。しかし、その前に、package.json
のscripts
を Next.js 関連のコマンドに更新し、.next
とnext-env.d.ts
を.gitignore
に追加する必要があります:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
# ...
.next
next-env.d.ts
dist
次にnpm run dev
を実行し、http://localhost:3000
を開いてください。これでアプリケーションが Next.js 上で実行されているのを見ることができるはずです。
例: Next.js に移行した CRA アプリケーションの動作例は、このリポジトリ をご覧ください。
Step 9: Clean Up
あなたは今、Create React App に関連するアーティファクトから Code ベースをクリーンアップすることができます:
- Delete
src/index.tsx
- Delete
public/index.html
を削除します - Delete
reportWebVitals
セットアップを削除 react-scripts
の CRA 依存関係をアンインストールします。
Bundler Compatibility
Create React App と Next.js の両方がバンドルのために webpack を default として使用します。
あなたが CRA アプリケーションを Next.js に移行する際、移行したいカスタムの webpack 設定があるかもしれません。Next.js は、カスタムの webpack 設定を提供することをサポートしています。
さらに、Next.js は next dev --turbo
を通じて Turbopack をサポートし、ローカルの dev パフォーマンスを向上させます。さらに、Turbopack は互換性と段階的な採用のために、いくつかの webpack ローダー もサポートしています。
Next Steps
すべてが計画通りに進んだ場合、あなたは今、シングルページアプリケーションとして機能する Next.js アプリケーションを持っています。しかし、まだ Next.js の多くの利点を活用していませんが、これから段階的な変更を加えてすべての利点を享受することができます。次に行うかもしれないことはこちらです:
- React Router からNext.js App Routerに移行して以下を得る:
<Image>
コンポーネントで画像を最適化するnext/font
でフォントを最適化する<Script>
コンポーネントでサードパーティスクリプトを最適化する- ESLint の設定を更新して Next.js のルールをサポートする
Good to know: 静的な export を使用すると、
useParams
hook は、現在サポートされていません 。