Sponsor
ChatHubChatHub Use GPT-4, Gemini, Claude 3.5 and more chatbots side-by-side
ここをクリック
Menu

App Routerインクリメンタル導入ガイド

このガイドは以下の内容をサポートします:

アップグレード

Node.jsバージョン

最小Node.jsバージョンは現在v18.17です。詳細についてはNode.jsドキュメントを参照してください。

Next.jsバージョン

Next.jsバージョン13に更新するには、お好みのパッケージマネージャーを使用して次のコマンドを実行してください:

Terminal
npm install next@latest react@latest react-dom@latest

ESLintバージョン

ESLintを使用している場合は、ESLintバージョンをアップグレードする必要があります:

Terminal
npm install -D eslint-config-next@latest

補足: ESLintの変更を有効にするには、VS CodeでESLintサーバーを再起動する必要がある場合があります。コマンドパレット(Macではcmd+shift+p、Windowsではctrl+shift+p)を開き、ESLint: Restart ESLint Serverを検索してください。

次のステップ

更新後、以下のセクションを参照して次のステップを確認してください:

新機能のアップグレード

Next.js 13では、新しい機能と規約を持つ新しいApp Routerが導入されました。新しいRouterはappディレクトリで使用でき、pagesディレクトリと共存できます。

Next.js 13へのアップグレードは、App Routerの使用を必要としません。更新されたImageコンポーネントLinkコンポーネントScriptコンポーネントフォント最適化など、両方のディレクトリで動作する新機能を使用して、pagesを引き続き使用できます。

<Image/> コンポーネント

Next.js 12では、next/future/imageという一時的なインポートを使用して、Imageコンポーネントに新しい改良が導入されました。これらの改良には、クライアントサイドJavaScriptの削減、画像の拡張とスタイル設定のより簡単な方法、アクセシビリティの向上、ブラウザのネイティブ遅延読み込みなどが含まれています。

バージョン13では、この新しい動作がnext/imageのデフォルトになりました。

新しいImageコンポーネントへの移行を支援するためのコードモッドが2つあります:

  • next-image-to-legacy-image コードモッドnext/imageインポートをnext/legacy/imageに安全かつ自動的に名前変更します。既存のコンポーネントは同じ動作を維持します。
  • next-image-experimental コードモッド:インラインスタイルを危険に追加し、未使用のpropsを削除します。これにより、既存のコンポーネントの動作が変更され、新しいデフォルトに一致します。このコードモッドを使用するには、まずnext-image-to-legacy-imageコードモッドを実行する必要があります。

<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 コードモッドを使用できます。

<Script> コンポーネント

next/scriptの動作が更新され、pagesappの両方をサポートするようになりましたが、スムーズな移行を確保するためにいくつかの変更が必要です:

  • 以前に_document.jsに含めていたbeforeInteractiveスクリプトをルートレイアウトファイル(app/layout.tsx)に移動します。
  • 実験的なworker戦略は現在appでは機能せず、この戦略で指定されたスクリプトは削除するか、別の戦略(例:lazyOnload)を使用するように変更する必要があります。
  • onLoadonReadyonErrorハンドラーはServer Componentsでは機能しないため、Client Componentに移動するか、完全に削除する必要があります。

フォント最適化

以前、Next.jsはフォントCSSのインライン化によってフォントの最適化をサポートしていました。バージョン13では、新しいnext/fontモジュールが導入され、優れたパフォーマンスとプライバシーを確保しながら、フォントの読み込み体験をカスタマイズする機能が提供されます。next/fontpagesappの両方のディレクトリでサポートされています。

CSSのインライン化pagesではまだ機能しますが、appでは機能しません。代わりにnext/fontを使用する必要があります。

next/fontの使用方法については、フォント最適化ページを参照してください。

pagesからappへの移行

🎥 視聴: App Routerを段階的に導入する方法を学ぶ → YouTube (16分)

App Routerへの移行は、Server Components、Suspense、およびその他のReact機能をNext.jsが活用する初めての機会かもしれません。特殊ファイルレイアウトなどのNext.jsの新機能と組み合わせると、移行には新しい概念、メンタルモデル、および動作の変化を学ぶ必要があります。

これらの更新の複雑さを軽減するために、移行を小さなステップに分割することをお勧めします。appディレクトリはpagesディレクトリと同時に動作するように意図的に設計されており、ページごとの段階的な移行が可能です。

  • appディレクトリはネストされたルートとレイアウトをサポートします。詳細はこちら
  • ネストされたフォルダを使用してルートを定義し、特殊なpage.jsファイルを使用してルートセグメントを公開アクセス可能にします。詳細はこちら
  • 特殊ファイル規則を使用して、各ルートセグメントのUIを作成します。最も一般的な特殊ファイルはpage.jslayout.jsです。
    • page.jsを使用して、ルートに固有のUIを定義します。
    • layout.jsを使用して、複数のルート間で共有されるUIを定義します。
    • 特殊ファイルには.js.jsx、または.tsxファイル拡張子を使用できます。
  • コンポーネント、スタイル、テストなどの他のファイルをappディレクトリ内に配置できます。詳細はこちら
  • getServerSidePropsgetStaticPropsなどのデータ取得関数は、app内の新しいAPIに置き換えられました。getStaticPathsgenerateStaticParamsに置き換えられました。
  • pages/_app.jspages/_document.jsは、単一のapp/layout.jsルートレイアウトに置き換えられました。詳細はこちら
  • pages/_error.jsは、より細かいerror.js特殊ファイルに置き換えられました。詳細はこちら
  • pages/404.jsnot-found.jsファイルに置き換えられました。
  • pages/api/* APIルートはroute.js(ルートハンドラー)特殊ファイルに置き換えられました。

ステップ1:appディレクトリの作成

最新のNext.jsバージョンに更新します(13.4以上が必要):

npm install next@latest

その後、プロジェクトのルート(またはsrc/ディレクトリ)に新しいappディレクトリを作成します。

ステップ2:ルートレイアウトの作成

appディレクトリ内に新しいapp/layout.tsxファイルを作成します。これはapp内のすべてのルートに適用されるルートレイアウトです。

app/layout.tsx
TypeScript
export default function RootLayout({
  // レイアウトはchildren propを受け取る必要があります
  // これはネストされたレイアウトやページで埋められます
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • appディレクトリには必ずルートレイアウトを含める必要があります。
  • ルートレイアウトは<html><body>タグを定義する必要があります。Next.jsは自動的にこれらを作成しません。
  • ルートレイアウトはpages/_app.tsxpages/_document.tsxファイルに代わるものです。
  • レイアウトファイルには.js.jsx、または.tsx拡張子を使用できます。

<head> HTML要素を管理するには、組み込みのSEOサポートを使用できます:

app/layout.tsx
TypeScript
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}

_document.js_app.jsの移行

既存の_appまたは_documentファイルがある場合は、その内容(グローバルスタイルなど)をルートレイアウト(app/layout.tsx)にコピーできます。app/layout.tsxのスタイルはpages/*には適用されません。移行中は_app/_documentを維持して、pages/*ルートが壊れないようにする必要があります。完全に移行してから、安全に削除できます。

React Contextプロバイダーを使用している場合は、Client Componentに移動する必要があります。

getLayout()パターンからレイアウトへの移行(任意)

Next.jsでは、pagesディレクトリでページごとのレイアウトを実現するためにページコンポーネントにプロパティを追加することを推奨していました。このパターンは、appディレクトリのネストされたレイアウトのネイティブサポートに置き換えることができます。

変更前と変更後の例を見る

変更前

components/DashboardLayout.js
export default function DashboardLayout({ children }) {
  return (
    <div>
      <h2>My Dashboard</h2>
      {children}
    </div>
  )
}
pages/dashboard/index.js
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.js
    export 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>
      )
    }
  • DashboardLayoutappディレクトリ内の新しいlayout.jsファイルにインポートします。

    app/dashboard/layout.js
    import DashboardLayout from './DashboardLayout'
     
    // これはServer Componentです
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>
    }
  • クライアントに送信するコンポーネントJavaScriptの量を減らすために、DashboardLayout.js(Client Component)の非インタラクティブな部分をlayout.js(Server Component)に段階的に移動できます。

ステップ3:next/headの移行

pagesディレクトリでは、next/head Reactコンポーネントを使用して<title><meta>などの<head> HTML要素を管理しています。appディレクトリでは、next/headは新しい組み込みSEOサポートに置き換えられました。

変更前:

pages/index.tsx
TypeScript
import Head from 'next/head'
 
export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}

変更後:

app/page.tsx
TypeScript
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'My Page Title',
}
 
export default function Page() {
  return '...'
}

すべてのmetadataオプションを見る

ステップ4:ページの移行

  • appディレクトリのページはデフォルトでServer Componentsです。これは、ページがClient Componentsであるpagesディレクトリとは異なります。
  • データ取得appで変更されました。getServerSidePropsgetStaticPropsgetInitialPropsはよりシンプルなAPIに置き換えられました。
  • appディレクトリはネストされたフォルダを使用してルートを定義し、特殊なpage.jsファイルを使用してルートセグメントを公開アクセス可能にします。
  • pagesディレクトリappディレクトリルート
    index.jspage.js/
    about.jsabout/page.js/about
    blog/[slug].jsblog/[slug]/page.js/blog/post-1

ページの移行を主に2つのステップに分けることをお勧めします:

  • ステップ1:デフォルトエクスポートされたPageコンポーネントを新しいClient Componentに移動します。
  • ステップ2:新しいClient Componentをappディレクトリ内の新しいpage.jsファイルにインポートします。

補足: これはpagesディレクトリと最も比較可能な動作を持つため、最も簡単な移行パスです。

ステップ1:新しいClient Componentを作成する

  • appディレクトリ内に新しい別ファイル(例:app/home-page.tsxなど)を作成し、Client Componentをエクスポートします。Client Componentを定義するには、ファイルの先頭(インポートの前)に'use client'ディレクティブを追加します。
    • Pages Routerと同様に、初回ページ読み込み時にClient Componentを静的HTMLに事前レンダリングする最適化ステップがあります。
  • pages/index.jsからデフォルトエクスポートされたページコンポーネントをapp/home-page.tsxに移動します。
app/home-page.tsx
TypeScript
'use client'
 
// これはClient Componentです(`pages`ディレクトリのコンポーネントと同じ)
// propsとしてデータを受け取り、状態とエフェクトにアクセスでき、
// 初回ページ読み込み時にサーバー上で事前レンダリングされます。
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に直接移動します。詳細についてはデータ取得アップグレードガイドを参照してください。

    app/page.tsx
    TypeScript
    // 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からインポートされ、next/routerからインポートされるpagesuseRouterフックとは異なる動作をします。
  • 新しいuseRouterpathname文字列を返しません。代わりに別のusePathnameフックを使用してください。
  • 新しいuseRouterqueryオブジェクトを返しません。検索パラメータと動的ルートパラメータは現在別々になっています。代わりにuseSearchParamsuseParamsフックを使用してください。
  • useSearchParamsusePathnameを一緒に使用して、ページの変更をリッスンできます。詳細についてはRouter Eventsセクションを参照してください。
  • これらの新しいフックはClient Componentsでのみサポートされています。Server Componentsでは使用できません。
app/example-client-component.tsx
TypeScript
'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置き換えられたため削除されました。
  • localelocalesdefaultLocalesdomainLocalesの値は、appディレクトリでは組み込みのi18n Next.js機能が不要になったため削除されました。i18nについて詳しく学ぶ
  • basePathは削除されました。代替手段はuseRouterの一部ではありません。まだ実装されていません。
  • asPathは、新しいルーターからasの概念が削除されたため削除されました。
  • isReadyは不要になったため削除されました。静的レンダリング中にuseSearchParams()フックを使用するコンポーネントは、事前レンダリングステップをスキップし、代わりに実行時にクライアント上でレンダリングされます。
  • routeは削除されました。usePathnameまたはuseSelectedLayoutSegments()が代替手段を提供します。

useRouter()のAPIリファレンスを見る

pagesapp間でのコンポーネントの共有

ルーター間でコンポーネントを互換性を保つには、next/compat/routerからのuseRouterフックを参照してください。 これはpagesディレクトリのuseRouterフックですが、ルーター間でコンポーネントを共有する際に使用することを意図しています。appルーターだけで使用する準備ができたら、next/navigationからの新しいuseRouterに更新してください。

ステップ6:データ取得メソッドの移行

pagesディレクトリではページのデータ取得にgetServerSidePropsgetStaticPropsを使用しています。appディレクトリでは、これらの以前のデータ取得関数はfetch()async React Server Componentsの上に構築されたよりシンプルなAPIに置き換えられました。

app/page.tsx
TypeScript
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コンポーネントにプロップを転送するために使用されます。ページの初期HTMLはサーバーから事前レンダリングされ、その後ブラウザで「ハイドレーション」(インタラクティブにする)されます。

pages/dashboard.js
// `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コンポーネント内でデータ取得をコロケーションできます。これにより、サーバーからのレンダリングされたHTMLを維持しながら、クライアントに送信するJavaScriptを減らすことができます。

cacheオプションをno-storeに設定することで、取得したデータをキャッシュしないように指示できます。これはpagesディレクトリのgetServerSidePropsと同様です。

app/dashboard/page.tsx
TypeScript
// `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オブジェクトを取得し、それを使用してリクエストのクッキーとヘッダーを取得できます。

pages/index.js
// `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内でクッキーを取得するために使用できます。
app/page.tsx
TypeScript
// `app`ディレクトリ
import { cookies, headers } from 'next/headers'
 
async function getData() {
  const authHeader = (await headers()).get('authorization')
 
  return '...'
}
 
export default async function Page() {
  // Server Components内で直接または
  // データ取得関数内で`cookies`または`headers`を使用できます
  const theme = (await cookies()).get('theme')
  const data = await getData()
  return '...'
}

静的サイト生成(getStaticProps

pagesディレクトリでは、getStaticProps関数を使用してビルド時にページを事前レンダリングします。この関数を使用して外部APIやデータベースから直接データを取得し、ビルド中にページが生成されるときにこのデータをページ全体に渡すことができます。

pages/index.js
// `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/page.js
// `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/posts/[id].js
// `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ディレクトリでは、getStaticPathsgenerateStaticParamsに置き換えられました。

generateStaticParamsgetStaticPathsと同様に動作しますが、ルートパラメータを返すためのAPIが簡略化され、レイアウト内で使用できます。generateStaticParamsの戻り値の形は、ネストされたparamオブジェクトの配列や解決されたパスの文字列ではなく、セグメントの配列です。

app/posts/[id]/page.js
// `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} />
}

generateStaticParamsという名前を使用することは、appディレクトリの新しいモデルにとってgetStaticPathsよりも適切です。getプレフィックスは、getStaticPropsgetServerSidePropsがもはや必要ないため、より説明的なgenerateに置き換えられました。Pathsサフィックスは、複数の動的セグメントを持つネストされたルーティングにより適切なParamsに置き換えられました。

fallbackの置き換え

pagesディレクトリでは、getStaticPathsから返されるfallbackプロパティは、ビルド時に事前レンダリングされないページの動作を定義するために使用されます。このプロパティは、ページが生成されている間にフォールバックページを表示するtrue、404ページを表示するfalse、またはリクエスト時にページを生成するblockingに設定できます。

pages/posts/[id].js
// `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ディレクトリのgetStaticPathsfallback: true | false | 'blocking'オプションを置き換えます。ストリーミングでは'blocking'trueの違いは無視できるため、dynamicParamsにはfallback: 'blocking'オプションは含まれていません。

app/posts/[id]/page.js
// `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 ...
}

dynamicParamstrue(デフォルト)に設定すると、生成されていないルートセグメントが要求された場合、サーバーレンダリングされてキャッシュされます。

インクリメンタル静的再生成(revalidate付きのgetStaticProps

pagesディレクトリでは、getStaticProps関数にrevalidateフィールドを追加して、一定時間後にページを自動的に再生成することができます。

pages/index.js
// `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/page.js
// `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ディレクトリではルートハンドラーに置き換えられました。

ルートハンドラーを使用すると、Web RequestResponse APIを使用して、特定のルートのカスタムリクエストハンドラーを作成できます。

app/api/route.ts
TypeScript
export async function GET(request: Request) {}

補足: 以前にクライアントから外部APIを呼び出すためにAPIルートを使用していた場合は、代わりにServer Componentsを使用してデータを安全に取得できるようになりました。データ取得についての詳細をご覧ください。

シングルページアプリケーション

同時にシングルページアプリケーション(SPA)からNext.jsに移行する場合は、詳細を学ぶためにドキュメントを参照してください。

ステップ7:スタイリング

pagesディレクトリでは、グローバルスタイルシートはpages/_app.jsのみに制限されていました。appディレクトリではこの制限が解除されました。グローバルスタイルは任意のレイアウト、ページ、またはコンポーネントに追加できます。

Tailwind CSS

Tailwind CSSを使用している場合は、tailwind.config.jsファイルにappディレクトリを追加する必要があります:

tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- この行を追加
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}

また、app/layout.jsファイルでグローバルスタイルをインポートする必要があります:

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間のナビゲーションを最適化して、プリフェッチされた高速なページ遷移を維持できます。詳細はこちら

コードモッド

Next.jsは、機能が非推奨になったときにコードベースのアップグレードを支援するためのCodemod変換を提供しています。詳細についてはコードモッドを参照してください。