Pages から App Router への移行方法
このガイドでは、以下のことをサポートします:
- Next.js アプリケーションをバージョン 12 からバージョン 13 に更新する
pages
ディレクトリとapp
ディレクトリの両方で動作する機能をアップグレードする- 既存のアプリケーションを
pages
からapp
に段階的に移行する
アップグレード
Node.js バージョン
最低限必要な Node.js のバージョンは v18.17 です。詳細は Node.js のドキュメントを参照してください。
Next.js バージョン
Next.js バージョン 13 に更新するには、お好みのパッケージマネージャーを使用して次のコマンドを実行してください:
npm install next@latest react@latest react-dom@latest
ESLint バージョン
ESLint を使用している場合、ESLint のバージョンをアップグレードする必要があります:
npm install -D eslint-config-next@latest
Good to know: ESLint の変更を反映させるために、VS Code で ESLint サーバーを再起動する必要があるかもしれません。コマンドパレット(Mac では
cmd+shift+p
、Windows ではctrl+shift+p
)を開き、ESLint: Restart ESLint Server
を検索してください。
次のステップ
更新が完了したら、次のセクションを参照して次のステップを確認してください:
- 新機能のアップグレード:改良された Image コンポーネントや Link コンポーネントなどの新機能にアップグレードするためのガイド。
pages
からapp
ディレクトリへの移行:pages
からapp
ディレクトリに段階的に移行するためのステップバイステップガイド。
新機能のアップグレード
Next.js 13 では、新しい App Router が新機能と規約と共に導入されました。新しい Router は app
ディレクトリで利用可能で、pages
ディレクトリと共存します。
Next.js 13 へのアップグレードは、App Router の使用を必須としません。pages
を引き続き使用し、両方のディレクトリで動作する新機能を利用できます。例えば、更新された Image コンポーネント、Link コンポーネント、Script コンポーネント、および Font 最適化 などです。
<Image/>
コンポーネント
Next.js 12 では、next/future/image
という一時的なインポートを使用して Image コンポーネントに新しい改善が導入されました。これらの改善には、クライアントサイドの JavaScript の削減、画像の拡張やスタイルの簡素化、アクセシビリティの向上、ネイティブブラウザの遅延読み込みが含まれます。
バージョン 13 では、この新しい動作が next/image
のデフォルトになりました。
新しい Image コンポーネントへの移行を支援するために、2 つの codemod が用意されています:
next-image-to-legacy-image
codemod:next/image
インポートをnext/legacy/image
に安全かつ自動的にリネームします。既存のコンポーネントは同じ動作を維持します。next-image-experimental
codemod:インラインスタイルを危険に追加し、未使用の props を削除します。これにより、既存のコンポーネントの動作が新しいデフォルトに一致するように変更されます。この codemod を使用するには、最初にnext-image-to-legacy-image
codemod を実行する必要があります。
<Link>
コンポーネント
<Link>
コンポーネント は、子として <a>
タグを手動で追加する必要がなくなりました。この動作は バージョン 12.2 で実験的なオプションとして追加され、現在はデフォルトです。Next.js 13 では、<Link>
は常に <a>
をレンダリングし、props を基礎となるタグに転送することができます。
例えば:
import Link from 'next/link'
// Next.js 12: `<a>` はネストされていないと除外されます
<Link href="/about">
<a>About</a>
</Link>
// Next.js 13: `<Link>` は常に内部で `<a>` をレンダリングします
<Link href="/about">
About
</Link>
Next.js 13 にリンクをアップグレードするには、new-link
codemod を使用できます。
<Script>
コンポーネント
next/script
の動作は、pages
と app
の両方をサポートするように更新されましたが、スムーズな移行を確保するためにいくつかの変更が必要です:
- 以前
_document.js
に含めていたbeforeInteractive
スクリプトを root レイアウトファイル(app/layout.tsx
)に移動します。 - 実験的な
worker
戦略はapp
ではまだ動作しないため、この戦略で示されたスクリプトは削除するか、別の戦略(例:lazyOnload
)を使用するように変更する必要があります。 onLoad
、onReady
、onError
ハンドラは Server Components では動作しないため、Client Component に移動するか、完全に削除する必要があります。
Font 最適化
以前は、Next.js はフォント CSS をインライン化することでフォントを最適化していました。バージョン 13 では、新しい next/font
モジュールが導入され、フォントの読み込み体験をカスタマイズしながら、優れたパフォーマンスとプライバシーを確保することができます。next/font
は pages
と app
の両方のディレクトリでサポートされています。
pages
では CSS のインライン化がまだ機能しますが、app
では機能しません。代わりに next/font
を使用する必要があります。
next/font
の使用方法については、Font 最適化 ページを参照してください。
pages
から app
への移行
🎥 視聴: App Router を段階的に採用する方法を学ぶ → YouTube (16 分)。
App Router への移行は、Next.js が基盤として構築する React の機能(Server Components、Suspense など)を初めて使用することを意味するかもしれません。新しい Next.js の機能(特別なファイル や レイアウト など)と組み合わせることで、移行は新しい概念、メンタルモデル、学ぶべき動作の変化を意味します。
これらの更新の複雑さを軽減するために、移行を小さなステップに分解することをお勧めします。app
ディレクトリは、pages
ディレクトリと同時に動作するように意図的に設計されており、ページごとに段階的に移行することができます。
app
ディレクトリはネストされたルート と レイアウトをサポートしています。詳細はこちら。- ネストされたフォルダを使用してルートを定義し、特別な
page.js
ファイルを使用してルートセグメントを公開します。詳細はこちら。 - 特別なファイルの規約 は、各ルートセグメントの UI を作成するために使用されます。最も一般的な特別なファイルは
page.js
とlayout.js
です。page.js
を使用して、ルートに固有の UI を定義します。layout.js
を使用して、複数のルートに共通する UI を定義します。- 特別なファイルには
.js
、.jsx
、または.tsx
の拡張子を使用できます。
- コンポーネント、スタイル、テストなどの他のファイルを
app
ディレクトリ内に配置することができます。詳細はこちら。 getServerSideProps
やgetStaticProps
などのデータフェッチ関数は、app
内の 新しい API に置き換えられました。getStaticPaths
はgenerateStaticParams
に置き換えられました。pages/_app.js
とpages/_document.js
は、単一のapp/layout.js
root レイアウトに置き換えられました。詳細はこちら。pages/_error.js
は、より細かいerror.js
特別なファイルに置き換えられました。詳細はこちら。pages/404.js
はnot-found.js
ファイルに置き換えられました。pages/api/*
API Routes は、route.js
(Route Handler)特別なファイルに置き換えられました。
ステップ 1: app
ディレクトリの作成
最新の Next.js バージョンに更新します(13.4 以上が必要です):
npm install next@latest
次に、プロジェクトの root(または src/
ディレクトリ)に新しい app
ディレクトリを作成します。
ステップ 2: Root レイアウトの作成
app
ディレクトリ内に新しい app/layout.tsx
ファイルを作成します。これは、app
内のすべてのルートに適用される root レイアウト です。
- TypeScript
- JavaScript
export default function RootLayout({
// レイアウトは children prop を受け入れる必要があります。
// これはネストされたレイアウトやページで埋められます
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
export default function RootLayout({
// レイアウトは children prop を受け入れる必要があります。
// これはネストされたレイアウトやページで埋められます
children,
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
app
ディレクトリには 必ず root レイアウトが含まれている必要があります。- root レイアウトは
<html>
と<body>
タグを定義する必要があります。Next.js は自動的にそれらを作成しません。 - root レイアウトは
pages/_app.tsx
とpages/_document.tsx
ファイルを置き換えます。 - レイアウトファイルには
.js
、.jsx
、または.tsx
の拡張子を使用できます。
<head>
HTML 要素を管理するには、組み込みの SEO サポート を使用できます:
- TypeScript
- JavaScript
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
export const metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
_document.js
と _app.js
の移行
既存の _app
または _document
ファイルがある場合、その内容(例:グローバルスタイル)を root レイアウト(app/layout.tsx
)にコピーできます。app/layout.tsx
のスタイルは pages/*
には適用されません。移行中に pages/*
ルートが壊れないように _app
/_document
を保持する必要があります。完全に移行が完了したら、それらを安全に削除できます。
React Context プロバイダーを使用している場合、それらを Client Component に移動する必要があります。
getLayout()
パターンをレイアウトに移行する(オプション)
Next.js は、pages
ディレクトリでページごとのレイアウトを実現するために Page コンポーネントにプロパティを追加することを推奨していました。このパターンは、app
ディレクトリでの ネストされたレイアウト のネイティブサポートに置き換えることができます。
前後の例を参照
前
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
}
import DashboardLayout from '../components/DashboardLayout'
export default function Page() {
return <p>My Page</p>
}
Page.getLayout = function getLayout(page) {
return <DashboardLayout>{page}</DashboardLayout>
}
後
-
pages/dashboard/index.js
からPage.getLayout
プロパティを削除し、app
ディレクトリへの ページ移行の手順 に従います。app/dashboard/page.jsexport default function Page() {
return <p>My Page</p>
} -
DashboardLayout
の内容を新しい Client Component に移動して、pages
ディレクトリの動作を保持します。app/dashboard/DashboardLayout.js'use client' // このディレクティブはファイルの先頭、インポートの前に配置する必要があります。
// これは Client Component です
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
} -
DashboardLayout
をapp
ディレクトリ内の新しいlayout.js
ファイルにインポートします。app/dashboard/layout.jsimport DashboardLayout from './DashboardLayout'
// これは Server Component です
export default function Layout({ children }) {
return <DashboardLayout>{children}</DashboardLayout>
} -
DashboardLayout.js
(Client Component)の非インタラクティブな部分をlayout.js
(Server Component)に段階的に移動して、クライアントに送信するコンポーネント JavaScript の量を減らすことができます。
ステップ 3: next/head
の移行
pages
ディレクトリでは、next/head
React コンポーネントを使用して title
や meta
などの <head>
HTML 要素を管理します。app
ディレクトリでは、next/head
は新しい 組み込みの SEO サポート に置き換えられます。
前:
- TypeScript
- JavaScript
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}
後:
- TypeScript
- JavaScript
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}
export const metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}
ステップ 4: ページの移行
app
ディレクトリ のページはデフォルトで Server Components です。これは、pages
ディレクトリのページが Client Components であるのとは異なります。app
での データフェッチ は変更されました。getServerSideProps
、getStaticProps
、getInitialProps
はよりシンプルな API に置き換えられました。app
ディレクトリはネストされたフォルダを使用してルートを定義し、特別なpage.js
ファイルを使用してルートセグメントを公開します。-
pages
ディレクトリapp
ディレクトリルート index.js
page.js
/
about.js
about/page.js
/about
blog/[slug].js
blog/[slug]/page.js
/blog/post-1
ページの移行を 2 つの主要なステップに分解することをお勧めします:
- ステップ 1: デフォルトでエクスポートされた Page コンポーネントを新しい Client Component に移動します。
- ステップ 2: 新しい
page.js
ファイル内で新しい Client Component をインポートします。
Good to know: これは
pages
ディレクトリと最も比較可能な動作を持つため、最も簡単な移行パスです。
ステップ 1: 新しい Client Component を作成する
app
ディレクトリ内に新しいファイル(例:app/home-page.tsx
など)を作成し、Client Component をエクスポートします。Client Components を定義するには、ファイルの先頭(インポートの前)に'use client'
ディレクティブを追加します。- Pages Router と同様に、初回ページ読み込み時に Client Components を静的 HTML にプリレンダリングする 最適化ステップ があります。
pages/index.js
からデフォルトでエクスポートされたページコンポーネントをapp/home-page.tsx
に移動します。
- TypeScript
- JavaScript
'use client'
// これは Client Component です(`pages` ディレクトリのコンポーネントと同じ)
// データを props として受け取り、状態とエフェクトにアクセスでき、
// 初回ページ読み込み時にサーバーでプリレンダリングされます。
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
'use client'
// これは Client Component です。データを props として受け取り、
// 状態とエフェクトにアクセスでき、`pages` ディレクトリの Page コンポーネントと同様です。
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
ステップ 2: 新しいページを作成する
app
ディレクトリ内に新しいapp/page.tsx
ファイルを作成します。これはデフォルトで Server Component です。home-page.tsx
Client Component をページにインポートします。pages/index.js
でデータをフェッチしていた場合、新しい データフェッチ API を使用してデータフェッチロジックを直接 Server Component に移動します。詳細は データフェッチのアップグレードガイド を参照してください。
- TypeScript
- JavaScript
// Client Component をインポートします
import HomePage from './home-page'
async function getPosts() {
const res = await fetch('https://...')
const posts = await res.json()
return posts
}
export default async function Page() {
// Server Component で直接データをフェッチします
const recentPosts = await getPosts()
// フェッチしたデータを Client Component に転送します
return <HomePage recentPosts={recentPosts} />
}
// Client Component をインポートします
import HomePage from './home-page'
async function getPosts() {
const res = await fetch('https://...')
const posts = await res.json()
return posts
}
export default async function Page() {
// Server Component で直接データをフェッチします
const recentPosts = await getPosts()
// フェッチしたデータを Client Component に転送します
return <HomePage recentPosts={recentPosts} />
}
- 以前のページで
useRouter
を使用していた場合、新しいルーティングフックに更新する必要があります。詳細はこちら。 - 開発サーバーを起動し、
http://localhost:3000
にアクセスします。app
ディレクトリを通じて提供される既存のインデックスルートが表示されるはずです。
ステップ 5: ルーティングフックの移行
新しいルーターが app
ディレクトリの新しい動作をサポートするために追加されました。
app
では、next/navigation
からインポートされる 3 つの新しいフックを使用する必要があります:useRouter()
、usePathname()
、および useSearchParams()
。
- 新しい
useRouter
フックはnext/navigation
からインポートされ、pages
のuseRouter
フックとは異なる動作をします。pages
のuseRouter
フックはnext/router
からインポートされます。next/router
からインポートされるuseRouter
フック はapp
ディレクトリではサポートされていませんが、pages
ディレクトリでは引き続き使用できます。
- 新しい
useRouter
はpathname
文字列を返しません。代わりに、別のusePathname
フックを使用してください。 - 新しい
useRouter
はquery
オブジェクトを返しません。検索パラメータと動的ルートパラメータは別々になりました。代わりにuseSearchParams
とuseParams
フックを使用してください。 useSearchParams
とusePathname
を一緒に使用してページの変更を監視できます。詳細は Router Events セクションを参照してください。- これらの新しいフックは Client Components でのみサポートされています。Server Components では使用できません。
- TypeScript
- JavaScript
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
さらに、新しい useRouter
フックには以下の変更があります:
isFallback
は削除されました。fallback
は 置き換えられました。locale
、locales
、defaultLocales
、domainLocales
の値は削除されました。組み込みの i18n Next.js 機能はapp
ディレクトリでは不要です。i18n について詳しくはこちら。basePath
は削除されました。代替案はuseRouter
の一部ではありません。まだ実装されていません。asPath
は削除されました。新しいルーターからas
の概念が削除されたためです。isReady
は削除されました。もはや必要ありません。静的レンダリング 中に、useSearchParams()
フックを使用するコンポーネントはプリレンダリングステップをスキップし、代わりにクライアントで実行時にレンダリングされます。route
は削除されました。usePathname
またはuseSelectedLayoutSegments()
が代替手段を提供します。
pages
と app
間でのコンポーネントの共有
コンポーネントを pages
と app
ルーター間で互換性を保つために、next/compat/router
からの useRouter
フック を参照してください。
これは pages
ディレクトリからの useRouter
フックですが、ルーター間でコンポーネントを共有する際に使用することを意図しています。app
ルーターでのみ使用する準備ができたら、新しい next/navigation
からの useRouter
に更新してください。
ステップ 6: データフェッチメソッドの移行
pages
ディレクトリでは、getServerSideProps
と getStaticProps
を使用してページのデータをフェッチします。app
ディレクトリ内では、これらの以前のデータフェッチ関数は、fetch()
と async
React Server Components を基盤とした よりシンプルな API に置き換えられました。
- TypeScript
- JavaScript
export default async function Page() {
// このリクエストは手動で無効化されるまでキャッシュされるべきです。
// `getStaticProps` に似ています。
// `force-cache` はデフォルトであり、省略可能です。
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// このリクエストは毎回リフェッチされるべきです。
// `getServerSideProps` に似ています。
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// このリクエストは 10 秒の寿命でキャッシュされるべきです。
// `revalidate` オプションを持つ `getStaticProps` に似ています。
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}
export default async function Page() {
// このリクエストは手動で無効化されるまでキャッシュされるべきです。
// `getStaticProps` に似ています。
// `force-cache` はデフォルトであり、省略可能です。
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// このリクエストは毎回リフェッチされるべきです。
// `getServerSideProps` に似ています。
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// このリクエストは 10 秒の寿命でキャッシュされるべきです。
// `revalidate` オプションを持つ `getStaticProps` に似ています。
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}
サーバーサイドレンダリング (getServerSideProps
)
pages
ディレクトリでは、getServerSideProps
を使用してサーバー上でデータをフェッチし、ファイル内のデフォルトでエクスポートされた React コンポーネントに props を転送します。ページの初期 HTML はサーバーからプリレンダリングされ、その後ブラウザでページを「ハイドレート」(インタラクティブにする)します。
// `pages` ディレクトリ
export async function getServerSideProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Dashboard({ projects }) {
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
App Router では、Server Components を使用して React コンポーネント内にデータフェッチを配置できます。これにより、クライアントに送信する JavaScript の量を減らしながら、サーバーからレンダリングされた HTML を維持できます。
cache
オプションを no-store
に設定することで、フェッチされたデータが 決してキャッシュされない ことを示すことができます。これは pages
ディレクトリの getServerSideProps
に似ています。
- TypeScript
- JavaScript
// `app` ディレクトリ
// この関数は任意の名前を付けることができます
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
// `app` ディレクトリ
// この関数は任意の名前を付けることができます
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
リクエストオブジェクトへのアクセス
pages
ディレクトリでは、Node.js HTTP API に基づいてリクエストベースのデータを取得できます。
例えば、getServerSideProps
から req
オブジェクトを取得し、リクエストの cookie やヘッダーを取得することができます。
// `pages` ディレクトリ
export async function getServerSideProps({ req, query }) {
const authHeader = req.getHeaders()['authorization'];
const theme = req.cookies['theme'];
return { props: { ... }}
}
export default function Page(props) {
return ...
}
app
ディレクトリでは、リクエストデータを取得するための新しい読み取り専用関数が公開されています:
headers
:Web Headers API に基づいており、Server Components 内でリクエストヘッダーを取得するために使用できます。cookies
:Web Cookies API に基づいており、Server Components 内で cookie を取得するために使用できます。
- TypeScript
- JavaScript
// `app` ディレクトリ
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = (await headers()).get('authorization')
return '...'
}
export default async function Page() {
// `cookies` または `headers` を Server Components 内で
// 直接またはデータフェッチ関数内で使用できます
const theme = (await cookies()).get('theme')
const data = await getData()
return '...'
}
// `app` ディレクトリ
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = (await headers()).get('authorization')
return '...'
}
export default async function Page() {
// `cookies` または `headers` を Server Components 内で
// 直接またはデータフェッチ関数内で使用できます
const theme = (await cookies()).get('theme')
const data = await getData()
return '...'
}
静的サイト生成 (getStaticProps
)
pages
ディレクトリでは、getStaticProps
関数を使用してビルド時にページをプリレンダリングします。この関数は、外部 API からデータをフェッチしたり、データベースから直接データを取得したりして、このデータをページ全体に渡すために使用されます。
// `pages` ディレクトリ
export async function getStaticProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Index({ projects }) {
return projects.map((project) => <div>{project.name}</div>)
}
app
ディレクトリでは、fetch()
を使用したデータフェッチはデフォルトで cache: 'force-cache'
となり、リクエストデータが手動で無効化されるまでキャッシュされます。これは pages
ディレクトリの getStaticProps
に似ています。
// `app` ディレクトリ
// この関数は任意の名前を付けることができます
async function getProjects() {
const res = await fetch(`https://...`)
const projects = await res.json()
return projects
}
export default async function Index() {
const projects = await getProjects()
return projects.map((project) => <div>{project.name}</div>)
}
動的パス (getStaticPaths
)
pages
ディレクトリでは、getStaticPaths
関数を使用してビルド時にプリレンダリングされる動的パスを定義します。
// `pages` ディレクトリ
import PostLayout from '@/components/post-layout'
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
}
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
export default function Post({ post }) {
return <PostLayout post={post} />
}
app
ディレクトリでは、getStaticPaths
は generateStaticParams
に置き換えられました。
generateStaticParams
は getStaticPaths
と同様に動作しますが、ルートパラメータを返すためのシンプルな API を持ち、レイアウト 内で使用できます。generateStaticParams
の戻り値の形は、ネストされた param
オブジェクトの配列や解決されたパスの文字列ではなく、セグメントの配列です。
// `app` ディレクトリ
import PostLayout from '@/components/post-layout'
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }]
}
async function getPost(params) {
const res = await fetch(`https://.../posts/${(await params).id}`)
const post = await res.json()
return post
}
export default async function Post({ params }) {
const post = await getPost(params)
return <PostLayout post={post} />
}
app
ディレクトリの新しいモデルにおいて、generateStaticParams
という名前を使用することは getStaticPaths
よりも適切です。get
プレフィックスは、getStaticProps
や getServerSideProps
がもはや必要ないため、より説明的な generate
に置き換えられています。Paths
サフィックスは、複数の動的セグメントを持つネストされたルーティングに対してより適切な Params
に置き換えられています。
fallback
の置き換え
pages
ディレクトリでは、getStaticPaths
から返される fallback
プロパティを使用して、ビルド時にプリレンダリングされていないページの動作を定義します。このプロパティは、ページが生成される間にフォールバックページを表示するために true
に設定したり、404 ページを表示するために false
に設定したり、リクエスト時にページを生成するために blocking
に設定したりできます。
// `pages` ディレクトリ
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking'
};
}
export async function getStaticProps({ params }) {
...
}
export default function Post({ post }) {
return ...
}
app
ディレクトリでは、config.dynamicParams
プロパティ が generateStaticParams
の外でのパラメータの処理方法を制御します:
true
: (デフォルト)generateStaticParams
に含まれていない動的セグメントはオンデマンドで生成されます。false
:generateStaticParams
に含まれていない動的セグメントは 404 を返します。
これは pages
ディレクトリの getStaticPaths
の fallback: true | false | 'blocking'
オプションを置き換えます。dynamicParams
に fallback: 'blocking'
オプションは含まれていません。ストリーミングでは 'blocking'
と true
の違いはわずかです。
// `app` ディレクトリ
export const dynamicParams = true;
export async function generateStaticParams() {
return [...]
}
async function getPost(params) {
...
}
export default async function Post({ params }) {
const post = await getPost(params);
return ...
}
dynamicParams
が true
(デフォルト)に設定されている場合、生成されていないルートセグメントがリクエストされると、サーバーレンダリングされてキャッシュされます。
インクリメンタル静的再生成 (getStaticProps
with revalidate
)
pages
ディレクトリでは、getStaticProps
関数を使用して、一定の時間が経過した後にページを自動的に再生成するための revalidate
フィールドを追加できます。
// `pages` ディレクトリ
export async function getStaticProps() {
const res = await fetch(`https://.../posts`)
const posts = await res.json()
return {
props: { posts },
revalidate: 60,
}
}
export default function Index({ posts }) {
return (
<Layout>
<PostList posts={posts} />
</Layout>
)
}
app
ディレクトリでは、fetch()
を使用したデータフェッチで revalidate
を使用でき、指定された秒数の間リクエストをキャッシュします。
// `app` ディレクトリ
async function getPosts() {
const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
const data = await res.json()
return data.posts
}
export default async function PostList() {
const posts = await getPosts()
return posts.map((post) => <div>{post.name}</div>)
}
API ルート
API ルートは、pages/api
ディレクトリで変更なしに動作し続けます。ただし、app
ディレクトリでは Route Handlers に置き換えられました。
Route Handlers を使用すると、Web Request および Response API を使用して、特定のルートのカスタムリクエストハンドラを作成できます。
- TypeScript
- JavaScript
export async function GET(request: Request) {}
export async function GET(request) {}
Good to know: 以前にクライアントから外部 API を呼び出すために API ルートを使用していた場合、代わりに Server Components を使用してデータを安全にフェッチできます。データフェッチ について詳しく学びましょう。
シングルページアプリケーション
同時にシングルページアプリケーション(SPA)から Next.js に移行する場合は、ドキュメント を参照して詳細を学びましょう。
ステップ 7: スタイリング
pages
ディレクトリでは、グローバルスタイルシートは pages/_app.js
のみに制限されています。app
ディレクトリでは、この制限が解除されました。グローバルスタイルは任意のレイアウト、ページ、またはコンポーネントに追加できます。
Tailwind CSS
Tailwind CSS を使用している場合、tailwind.config.js
ファイルに app
ディレクトリを追加する必要があります:
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- この行を追加
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}
また、グローバルスタイルを app/layout.js
ファイルにインポートする必要があります:
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Tailwind CSS を使用したスタイリング について詳しく学びましょう。
App Router と Pages Router の併用
異なる Next.js ルーターによって提供されるルート間をナビゲートする際には、ハードナビゲーションが発生します。next/link
を使用した自動リンクプリフェッチは、ルーター間でプリフェッチしません。
代わりに、App Router と Pages Router 間のナビゲーションを最適化して、プリフェッチされた高速なページ遷移を維持できます。詳細はこちら。
Codemods
Next.js は、機能が廃止されたときにコードベースをアップグレードするのに役立つ Codemod 変換を提供します。詳細は Codemods を参照してください。