コンテンツにスキップ

App Routerの段階的導入ガイド

このガイドは以下に役立ちます

アップグレード

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

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

次のステップ

アップデートが完了したら、次のステップについて以下のセクションを参照してください。

新機能のアップグレード

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 つの 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> コンポーネントは、子として手動で <a> タグを追加する必要がなくなりました。この動作は、バージョン 12.2で実験的なオプションとして追加され、現在はデフォルトになっています。Next.js 13 では、<Link> は常に <a> をレンダリングし、基になるタグに props を転送できます。

例:

import Link from 'next/link'
 
// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  <a>About</a>
</Link>
 
// Next.js 13: `<Link>` always renders `<a>` under the hood
<Link href="/about">
  About
</Link>

Next.js 13 にリンクをアップグレードするには、new-link codemod を使用できます。

<Script> コンポーネント

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

  • 以前に _document.js に含めていた beforeInteractive スクリプトを、ルートレイアウトファイル (app/layout.tsx) に移動します。
  • 実験的な worker 戦略は、まだ app で機能しません。この戦略で示されたスクリプトは、削除するか、別の戦略 (例:lazyOnload) を使用するように変更する必要があります。
  • onLoadonReady、および onError ハンドラーは、サーバーコンポーネントでは機能しないため、それらを必ず クライアントコンポーネント に移動するか、完全に削除してください。

フォントの最適化

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

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

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

pages から app への移行

🎥 視聴: App Router を段階的に採用する方法をご覧ください → YouTube (16 分)

App Router への移行は、サーバーコンポーネント、Suspense など、Next.js が基盤とする React の機能を初めて使用する機会になる可能性があります。 特殊なファイルレイアウトなどの新しい Next.js 機能と組み合わせると、移行には新しい概念、メンタルモデル、および学習する行動の変化が伴います。

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

  • app ディレクトリは、ネストされたルートとレイアウトをサポートしています。詳細はこちら
  • ネストされたフォルダーを使用して ルートを定義し、特殊な page.js ファイルを使用してルートセグメントを公開します。詳細はこちら
  • 特殊なファイル規則は、各ルートセグメントの UI を作成するために使用されます。最も一般的な特殊なファイルは、page.jslayout.js です。
    • page.js を使用して、ルートに固有の UI を定義します。
    • layout.js を使用して、複数のルートで共有される UI を定義します。
    • 特殊なファイルには、.js.jsx、または .tsx のファイル拡張子を使用できます。
  • コンポーネント、スタイル、テストなど、その他のファイルを app ディレクトリ内に配置できます。詳細はこちら
  • getServerSidePropsgetStaticProps などのデータフェッチ関数は、app 内の 新しい API に置き換えられました。getStaticPaths は、generateStaticParams に置き換えられました。
  • pages/_app.jspages/_document.js は、単一の app/layout.js ルートレイアウトに置き換えられました。詳細はこちら
  • pages/_error.js は、より詳細な error.js 特殊ファイルに置き換えられました。詳細はこちら
  • pages/404.js は、not-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
export default function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • app ディレクトリには、必ずルートレイアウトを含める必要があります。
  • ルートレイアウトは、Next.jsが自動的に作成しないため、<html> タグと <body> タグを定義する必要があります。
  • ルートレイアウトは、pages/_app.tsx ファイルと pages/_document.tsx ファイルを置き換えます。
  • レイアウトファイルには、.js.jsx、または .tsx の拡張子が使用できます。

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

app/layout.tsx
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/* には適用されません。pages/* ルートが壊れるのを防ぐため、移行中は _app/_document を保持する必要があります。完全に移行したら、それらを安全に削除できます。

React Contextプロバイダーを使用している場合は、クライアントコンポーネントに移動する必要があります。

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>
    }
  • pages ディレクトリの動作を保持するために、DashboardLayout の内容を新しいクライアントコンポーネントに移動します。

    app/dashboard/DashboardLayout.js
    'use client' // this directive should be at top of the file, before any imports.
     
    // This is a 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'
     
    // This is a Server Component
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>
    }
  • DashboardLayout.js (クライアントコンポーネント) の非インタラクティブな部分を layout.js (サーバーコンポーネント) に段階的に移動して、クライアントに送信するコンポーネント JavaScript の量を減らすことができます。

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

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

移行前

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

移行後

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

すべてのメタデータオプションを参照.

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

  • app ディレクトリのページは、デフォルトでサーバーコンポーネントです。これは、ページがクライアントコンポーネントである pages ディレクトリとは異なります。
  • app では、データ取得が変更されています。getServerSidePropsgetStaticProps、および getInitialProps は、よりシンプルな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: デフォルトでエクスポートされるページコンポーネントを新しいクライアントコンポーネントに移動します。
  • ステップ 2: 新しいクライアントコンポーネントを、app ディレクトリ内の新しい page.js ファイルにインポートします。

知っておくと良いこと:これは、pages ディレクトリに最も近い動作を持つため、最も簡単な移行パスです。

ステップ 1: 新しいクライアントコンポーネントを作成する

  • app ディレクトリ内に新しい別のファイル(例:app/home-page.tsx など)を作成し、クライアントコンポーネントをエクスポートします。クライアントコンポーネントを定義するには、ファイルの先頭(インポートの前)に 'use client' ディレクティブを追加します。
    • ページルーターと同様に、クライアントコンポーネントを最初のページ読み込み時に静的HTMLにプリレンダリングするための最適化ステップがあります。
  • pages/index.js から app/home-page.tsx にデフォルトでエクスポートされるページコンポーネントを移動します。
app/home-page.tsx
'use client'
 
// This is a Client Component (same as components in the `pages` directory)
// It receives data as props, has access to state and effects, and is
// prerendered on the server during the initial page load.
export default function HomePage({ recentPosts }) {
  return (
    <div>
      {recentPosts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

ステップ 2: 新しいページを作成する

  • app ディレクトリ内に新しい app/page.tsx ファイルを作成します。これはデフォルトでサーバーコンポーネントです。

  • home-page.tsx クライアントコンポーネントをページにインポートします。

  • pages/index.js でデータを取得していた場合は、新しいデータ取得APIを使用して、データ取得ロジックをサーバーコンポーネントに直接移動します。詳細については、データ取得アップグレードガイドを参照してください。

    app/page.tsx
    // Import your 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() {
      // Fetch data directly in a Server Component
      const recentPosts = await getPosts()
      // Forward fetched data to your Client Component
      return <HomePage recentPosts={recentPosts} />
    }
  • 以前のページで useRouter を使用していた場合は、新しいルーティングフックに更新する必要があります。詳細はこちら

  • 開発サーバーを起動し、https://#:3000にアクセスします。appディレクトリを通じて提供されるようになった既存のインデックスルートが表示されます。

ステップ 5: ルーティングフックの移行

app ディレクトリの新しい動作をサポートするために、新しいルーターが追加されました。

app では、next/navigation からインポートされた3つの新しいフックを使用する必要があります: useRouter(), usePathname(), および useSearchParams().

  • 新しい useRouter フックは next/navigation からインポートされ、next/router からインポートされた pagesuseRouter フックとは異なる動作をします。
  • 新しい useRouterpathname 文字列を返しません。代わりに、別の usePathname フックを使用してください。
  • 新しい useRouterquery オブジェクトを返しません。検索パラメーターと動的ルートパラメーターは、現在は別々になっています。代わりに useSearchParams フックと useParams フックを使用してください。
  • useSearchParamsusePathname を組み合わせて、ページ変更をリッスンできます。詳細については、ルーターイベントのセクションを参照してください。
  • これらの新しいフックは、クライアントコンポーネントでのみサポートされています。サーバーコンポーネントでは使用できません。
app/example-client-component.tsx
'use client'
 
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
 
export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()
 
  // ...
}

さらに、新しい useRouter フックには、次の変更があります

  • fallback置き換えられたため、isFallback は削除されました。
  • localelocalesdefaultLocalesdomainLocales の値は、組み込みのi18n Next.js機能が app ディレクトリでは不要になったため、削除されました。i18nの詳細を参照してください。
  • basePath は削除されました。代替は useRouter の一部にはなりません。まだ実装されていません。
  • 新しいルーターから as の概念が削除されたため、asPath は削除されました。
  • 静的レンダリング中、useSearchParams()フックを使用するコンポーネントはプリレンダリングステップをスキップし、代わりに実行時にクライアントでレンダリングされるため、isReady は不要になったため削除されました。
  • route は削除されました。usePathname または useSelectedLayoutSegments() が代替を提供します。

useRouter() APIリファレンスを表示.

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

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

ステップ 6: データ取得方法の移行

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

app/page.tsx
export default async function Page() {
  // This request should be cached until manually invalidated.
  // Similar to `getStaticProps`.
  // `force-cache` is the default and can be omitted.
  const staticData = await fetch(`https://...`, { cache: 'force-cache' })
 
  // This request should be refetched on every request.
  // Similar to `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
 
  // This request should be cached with a lifetime of 10 seconds.
  // Similar to `getStaticProps` with the `revalidate` option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })
 
  return <div>...</div>
}

サーバーサイドレンダリング (getServerSideProps)

pages ディレクトリでは、getServerSideProps を使用してサーバーでデータを取得し、ファイルのデフォルトのエクスポートされた React コンポーネントにpropsを転送します。ページの初期HTMLはサーバーからプリレンダリングされ、その後ブラウザでページが「ハイドレーション」されます(インタラクティブになる)。

pages/dashboard.js
// `pages` directory
 
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と同様です。

app/dashboard/page.tsx
// `app` directory
 
// This function can be named anything
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` directory
 
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
// `app` directory
import { cookies, headers } from 'next/headers'
 
async function getData() {
  const authHeader = (await headers()).get('authorization')
 
  return '...'
}
 
export default async function Page() {
  // You can use `cookies` or `headers` inside Server Components
  // directly or in your data fetching function
  const theme = (await cookies()).get('theme')
  const data = await getData()
  return '...'
}

静的サイト生成 (getStaticProps)

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

pages/index.js
// `pages` directory
 
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` directory
 
// This function can be named anything
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` directory
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` directory
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/${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ディレクトリの新しいモデルでは、getStaticPathsよりもgenerateStaticParamsという名前を使用する方が適切です。getプレフィックスは、より説明的なgenerateに置き換えられました。これは、getStaticPropsgetServerSidePropsが不要になったため、単独で使用する方が適しています。PathsサフィックスはParamsに置き換えられました。これは、複数の動的セグメントを使用したネストされたルーティングにより適しています。


fallbackの置き換え

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

pages/posts/[id].js
// `pages` directory
 
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'オプションが置き換えられます。fallback: 'blocking'オプションは、ストリーミングを使用した場合、'blocking'trueの違いがごくわずかであるため、dynamicParamsには含まれていません。

app/posts/[id]/page.js
// `app` directory
 
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 (デフォルト) に設定されている場合、生成されていないルートセグメントがリクエストされると、サーバーレンダリングされ、キャッシュされます。

インクリメンタル静的再生成 (getStaticPropsrevalidate)

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

pages/index.js
// `pages` directory
 
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` directory
 
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 Request および Response APIを使用して、特定のルートのカスタムリクエストハンドラーを作成できます。

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

知っておくと便利: 以前にAPIルートを使用してクライアントから外部APIを呼び出していた場合は、Server Componentsを使用してデータを安全にフェッチできます。データ取得の詳細をご覧ください。

ステップ 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}', // <-- Add this line
    './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 を使用したスタイリングについてさらに詳しく学ぶ

Codemods

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