Lang x Lang

App Router Incremental Adoption Guide

このガイドはあなたを助けます:

Upgrading

Node.js Version

最小の Node.js version は現在v18.17です。詳細については、Node.js ドキュメンテーション をご覧ください。

Next.js Version

あなたの好みのパッケージマネージャーを使用して、Next.js version13 にアップデートするには、次のコマンドを実行してください。

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

ESLint Version

あなたが ESLint を使っている場合、あなたの ESLint version をアップグレードする必要があります:

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

Good to know: ESLint server の変更が反映されるためには、VS Code で ESLint server を再起動する必要があるかもしれません。コマンドパレット(Mac ではcmd+shift+p、Windows ではctrl+shift+p)を開き、ESLint: Restart ESLint Serverで検索してください。

Next Steps

更新が完了した後は、 next ステップについては以下のセクションをご覧ください:

Upgrading New Features

Next.js 13 は、新機能と規約を備えた新しいApp Routerを導入しました。新しい Router は app ディレクトリで利用可能で、pages ディレクトリと共存します。

Next.js 13 にアップグレードしても、新しいApp Routerを require で使用する必要はありません。新しい機能を使った pages の使用を続けることができます。それらの新機能は、アップデートされたImage componentLink componentScript component、そしてFont optimizationなど、どちらのディレクトリでも動作します。

<Image/> Component

Next.js 12 は、一時的な import:next/future/imageとともに、Image Component に新しい改良を導入しました。これらの改良には、Client 側の JavaScript の削減、画像の拡張や style をより簡単に行う方法、より良いアクセシビリティ、およびブラウザのネイティブな lazy loading が含まれています。

version13 では、この新しい動作が next/image の default になりました。

新しい Image Component に移行するための 2 つの codemods があります:

  • next-image-to-legacy-image codemod: 安全かつ自動で next/image のインポートを next/legacy/image に名前変更します。既存の Component は同じ動作を維持します。
  • next-image-experimental コードモッド: 危険なインラインの styles を追加し、未使用の props を削除します。これにより、既存のコンポーネントの動作が新しい default に合わせて変更されます。このコードモッドを使用するには、まず next-image-to-legacy-image コードモッドを実行する必要があります。

<Link> Componentは、子として<a>タグを手動で追加する必要がなくなりました。この動作は、version 12.2 で実験的なオプションとして追加され、現在は default となっています。Next.js 13 では、<Link>は常に<a>をレンダリングし、props を基礎となるタグに転送することを可能にします。

For example:

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> Component

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

  • 以前に_document.jsに含めていた全てのbeforeInteractiveの scripts を、root layout ファイル(app/layout.tsx)に移動してください。
  • 実験的なworker strategy はまだ appで動作せず、この strategy を指定した scripts は削除するか、別の strategy (例:lazyOnload)を使用するように変更する必要があります。
  • onLoadonReady、そして onError ハンドラーラーは Server Components で動作しませんので、それらをClient Componentに移動するか、完全に削除するように確認してください。

フォント最適化

以前、Next.js はinlining font CSSによってフォントの最適化を支援していました。Version 13 では、新たにnext/fontモジュールが導入され、優れたパフォーマンスとプライバシーを維持しながらフォント loading 経験をカスタマイズする能力を提供します。next/fontは、pagesおよびappディレクトリの両方でサポートされています。

pagesinlining CSSはまだ機能しますが、appでは機能しません。代わりにnext/fontを使用すべきです。

next/fontの使い方を学ぶために、Font Optimizationのページをご覧ください。

Migrating from pages to app

🎥 視聴: App Router を少しずつ採用する方法を学ぶ → YouTube (16 分) .

App Router への移行は、Next.js が上に構築する React の機能を初めて使用するかもしれません。これには Server Components、Suspense などが含まれます。 特別なファイルLayout などの新しい Next.js の機能と組み合わされると、移行は新しい概念、思考モデル、行動変更を学習することを意味します。

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

  • appディレクトリはネストされた routes Layout をサポートしています。詳しくはこちら
  • ネストされたフォルダを使用してroutes を定義すると、特別なpage.jsファイルを使用して route セグメントを公開することができます。詳しくはこちら
  • Special file conventionsは、それぞれの route セグメントの UI を作成するために使用されます。最も一般的な特別なファイルは、page.jslayout.jsです。
    • page.jsを使用して、route に固有の UI を定義します。
    • layout.jsを使用して、複数の routes で共有される UI を定義してください。
    • 特別なファイルには、.js.jsx、または.tsxのファイル拡張子を使用できます。
  • 他のファイルを app ディレクトリに共存させることができます。例えば、コンポーネント、 styles 、テストなどです。詳しくはこちら
  • getServerSidePropsgetStaticPropsのようなデータ取得機能は、app内の新しい APIリンクに置き換えられました。getStaticPathsgenerateStaticParamsに置き換えられました。
  • pages/_app.jspages/_document.js は一つのapp/layout.js root layout に置き換えられました。もっと詳しく
  • pages/_error.jsは、より詳細なerror.js特別ファイルに置き換えられました。詳細を学ぶ
  • pages/404.jsnot-found.jsファイルに置き換えられました。
  • pages/api/* API Routes が特別なファイル route.js(Route Handler)に置き換えられました。

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

最新の Next.js version に更新してください(13.4 以上が必要):

npm install next@latest

次に、プロジェクトの root(またはsrc/ディレクトリ)に新しいappディレクトリを作成してください。

ステップ 2:Root Layout の作成

新しい app/layout.tsx ファイルを app ディレクトリの中に作成してください。これは、app 内のすべての routes に適用されるroot layoutです。

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/layout.js
export default function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • appディレクトリには、root layout が必ず含まれていなければなりません。
  • root layout は、<html><body>タグを定義する必要があります。なぜなら、Next.js はそれらを自動的に作成しないからです。
  • root layout は、pages/_app.tsxpages/_document.tsxファイルを置き換えます。
  • layout ファイルには、.js.jsx、または.tsxの拡張子を使用できます。

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

app/layout.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}
app/layout.js
export const metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}

_document.js_app.jsの移行

既存の _app または _document ファイルがある場合は、内容(例えば、グローバルな Styles )を root layout (app/layout.tsx) にコピーすることができます。 app/layout.tsx の styles は、 pages/* には適用されません。移行中は _app/_document を保持し、pages/* の routes が壊れるのを防ぐべきです。完全に移行が完了したら、それらを安全に delete することができます。

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

getLayout()パターンの Layouts への移行(オプショナル)

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

before/after の例を見る

Before

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>
}

After

  • Page.getLayout プロパティを pages/dashboard/index.js から削除し、ページのマイグレーション手順に従って app ディレクトリに移行してください。

    export default function Page() {
      return <p>My Page</p>;
    }
    
  • DashboardLayoutの内容を新しいClient Componentに移動して、pagesディレクトリの動作を維持してください。

    "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>
      );
    }
    
  • appディレクトリ内の新しいlayout.jsファイルにDashboardLayoutを Import してください。

    import DashboardLayout from "./DashboardLayout";
    
    // This is a Server Component
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>;
    }
    
  • DashboardLayout.jsの非インタラクティブな部分を( Client Component )からlayout.js( Server Component )に逐次移動させることで、 client に送信する component JavaScript の量を減らすことができます。

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

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

Before:

pages/index.tsx
import Head from 'next/head'

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

export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}

After:

app/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My Page Title',
}

export default function Page() {
  return '...'
}
app/page.js
export const metadata = {
  title: 'My Page Title',
}

export default function Page() {
  return '...'
}

すべての metadata options を参照してください。

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

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

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

  • ステップ 1:export された default ページ Component を新しい Client Component に移動します。
  • ステップ 2:新しいappディレクトリ内の新しいpage.jsファイルに新しい Client Component を Import します。

Good to know: これが最もpagesディレクトリに近い動作をするため、最も簡単な移行 path です。

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

  • appディレクトリ内に新しい別のファイル(例 app/home-page.tsx など)を作成し、その中に Client Component を export します。 Client Components を定義するには、ファイルの先頭(どのインポートよりも前)に'use client'ディレクティブを追加します。
    • Pages Router と同様に、初回のページロード時に Client Components を静的 HTML にプレレンダリングするための最適化ステップがあります。
  • pages/index.jsからapp/home-page.tsxへと、default という export されたページ component を移動させてください。
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>
  )
}
app/home-page.js
'use client'

// This is a Client Component. It receives data as props and
// has access to state and effects just like Page components
// in the `pages` directory.
export default function HomePage({ recentPosts }) {
  return (
    <div>
      {recentPosts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

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

  • 新しい app/page.tsx ファイルを app ディレクトリ内に作成します。これは、 default で Server Component です。

  • ページにhome-page.tsxの Import Client Component をインポートします。

  • pages/index.jsでデータを取得していた場合、新しいdata fetching APIsを使用してデータ取得のロジックを直接 Server Component に移動します。詳細はdata fetching upgrade guideを参照してください。

    // 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} />;
    }
    
    // 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 を使用していた場合、新しいルーティング hook への更新が必要です。 もっと詳しく

  • Start を押して、あなたの development server を始め、EXL0005 にアクセスしてください。すでに存在する index route が、今は app ディレクトリを通じて提供されているはずです。

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

新しい router が追加され、appディレクトリに新しい動作をサポートします。

appでは、next/navigationからインポートされた 3 つの新しい hook を使用する必要があります:useRouter()usePathname()、そしてuseSearchParams()

  • 新しいuseRouter hook は、next/navigationからインポートされ、next/routerからインポートされるpagesの中のuseRouter hook とは異なる動作を持っています。
    • next/routerからインポートされるuseRouter hookは、appディレクトリではサポートされていませんが、pagesディレクトリでは引き続き使用できます。
  • 新しいuseRouterpathnameの string を返しません。代わりに別のusePathnameの hook を使用してください。
  • 新しいuseRouterqueryの object を返しません。代わりに、別のuseSearchParamsの hook を使用してください。
  • useSearchParamsusePathname を同時に使用してページの変更をリスンすることができます。詳細はRouter Eventsセクションを参照してください。
  • これらの新しい hook は、Client Components でのみサポートされています。Server Components では使用できません。
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()

  // ...
}
app/example-client-component.js
'use client'

import { useRouter, usePathname, useSearchParams } from 'next/navigation'

export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()

  // ...
}

さらに、新たな useRouter hook には以下の変更があります:

  • isFallbackは削除されました。なぜなら、fallback置き換えられましたからです。
  • localelocalesdefaultLocalesdomainLocalesの値は、appディレクトリでビルトインの i18n Next.js 機能がもはや必要ないため削除されました。i18n についてもっと学ぶ
  • basePathは削除されました。代替手段はuseRouterの一部にはなりません。まだ実装されていません。
  • asPathは削除されました。なぜなら、新しい router からasの概念が削除されたからです。
  • isReadyはもはや必要ないため、削除されました。 static renderingの間に、useSearchParams() hook を使用するあらゆる component は、プレレンダリングステップをスキップし、代わりに client で runtime にレンダリングされます。

useRouter() API reference を見る

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

pages ディレクトリは、ページのデータを fetch するために 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>
}
app/page.js
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>
}

Server-side Rendering (getServerSideProps)

pagesディレクトリでは、getServerSidePropsが使われて server 上のデータを fetch し、props をファイル内の default で export された React component に転送します。このページの初期 HTML は server からプレレンダリングされ、次にブラウザでページを "hydrating"(インタラクティブにする)します。

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ディレクトリでは、Server Componentsを使用して、ReactComponent 内でデータの取得を共有できます。これにより、server からのレンダリングされた HTML を維持しながら、client へ送る JavaScript の量を減らすことができます。

cacheオプションをno-storeに設定することで、フェッチしたデータがnever キャッシュされるべきではないことを示すことができます。これは、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>
  )
}
app/dashboard/page.js
// `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>
  )
}

Request Object へのアクセス

pagesディレクトリでは、Node.js HTTP API に基づいて Request ベースのデータを取得できます。

たとえば、getServerSidePropsからreq object を取得し、リクエストの cookies や headers を取得するために使用することができます。

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ディレクトリは、request データを取得するための新しい読み取り専用の機能を公開します。

  • headers(): Web の Headers API に基づいており、Server Components内で request headers を取得するために使用できます。
  • cookies(): Web の Cookies API に基づき、Server Components内で cookies を取得するために使用できます。
app/page.tsx
// `app` directory
import { cookies, headers } from 'next/headers'

async function getData() {
  const authHeader = 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 = cookies().get('theme')
  const data = await getData()
  return '...'
}
app/page.js
// `app` directory
import { cookies, headers } from 'next/headers'

async function getData() {
  const authHeader = 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 = cookies().get('theme')
  const data = await getData()
  return '...'
}

Static Site Generation(getStaticProps)

pagesディレクトリでは、getStaticProps関数が使用されて、build の時にページを事前にレンダリングします。この関数を使用すると、外部の API からまたは直接データベースからデータを fetch して、そのデータをページ全体に渡すことができます。これは、build の過程で生成されています。

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()によるデータフェッチングは default で cache: 'force-cache' に設定され、request データが手動で無効にされるまで 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>)
}

Dynamic paths (getStaticPaths)

pagesディレクトリでは、getStaticPaths関数を使用して、build 時に事前に描画するべき dynamic paths を定義します。

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 に置き換えられています。

generateStaticParamsは、getStaticPathsと同様の動作をしますが、route パラメータを返すための簡略化された API を持ち、layoutsの中で使用できます。generateStaticParamsの返り値の形状は、ネストされたparamObject の配列や解決された paths の string ではなく、セグメントの配列です。

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というサフィックスは、複数の dynamic セグメントを持つネストされたルーティングに適したParamsに置き換えられました。


fallbackの置き換え

pagesディレクトリでは、getStaticPathsから返されるfallbackプロパティを使用して、 build 時に事前にレンダリングされていないページの動作を定義します。このプロパティは、ページが生成されている間に fallback ページを表示するためにtrueに設定するか、404 ページを表示するためにfalseに設定するか、または request 時にページを生成するために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の外部にある params の処理方法を制御します。

  • true: ( default ) generateStaticParams に含まれていない Dynamic セグメントは、要求に応じて生成されます。
  • false: generateStaticParamsに含まれていない Dynamic セグメントは 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に設定されている場合(“ default )、生成されていない route セグメントが要求されたとき、それは servers で rendering され cache されます。

Incremental Static Regeneration (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 を使用することができ、これにより指定された秒数にわたって request を cache します。

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 Routes

pages/apiディレクトリでは、何も変わらずに API Routes が引き続き作動します。しかし、それらはappディレクトリ内のRoute Handlersに置き換えられました。

Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.

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

Good to know: 以前 APIroutes を使用して client から外部の API を呼び出していた場合、代わりにServer Componentsを使用して安全に fetch データを取得できるようになりました。 data fetchingについて詳しく学ぶ。

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

pagesディレクトリでは、グローバルスタイルシートはpages/_app.jsのみに制限されています。しかし、appディレクトリでは、この制限が解除されました。グローバルな styles は、任意の layout 、ページ、または component に追加することができます。

Tailwind CSS

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

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}',
  ],
}

また、グローバルな styles をあなたの app/layout.js ファイルに import する必要があります:

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を参照してください。

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use