POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

FeedlyRSSTwitterFacebook
Lokman Musliu

本記事は、原著者の許諾のもとに翻訳・掲載しております。

Next.jsプロジェクトをアップグレードする

Next.jsの従来のPages Routerから新しいApp Routerに移行しましょう。ここの移行により、アプリケーションのルーティング効率と柔軟性が向上します。App Routerは、ファイルシステムベースのルーティング機能が改善されたほか、React Server Componentsが導入されたことなどにより、開発体験を向上させます。

依存関係をチェックする

package.jsonファイルのバージョンが最新であることが重要です。依存関係をすべてチェックし、新しいApp Routerと互換性があることを確認しましょう。必要であればアップグレードしてください。この準備を行うことで、移行時に互換性の問題が発生するのを避けることができます。

依存関係はnpmを使用することで簡単にチェックできます。以下のコマンドを実行してください。

$ npm outdated

/appディレクトリを作成する

まずは、Next.jsプロジェクトのルートに新しい/appディレクトリを作成しましょう。App Routerのファイルやコンポーネントはすべてここに格納されます。

PagesフォルダからAppフォルダにファイルを移動する

サーバー上でレンダリングされるHTMLドキュメントは、/pages/_document.tsxファイルを使用してカスタマイズします。App Routerでは、/app/layout.tsxファイルがこの機能を担います。

  • /pages/_document.tsxの内容を/app/layout.tsxという新しいファイルにコピーします。

  • next/documentのインポートを削除し、<Html><Head><Main />の各コンポーネントを対応するHTMLコンポーネント(<html><head>{children})に置き換えます。

  • <NextScript />コンポーネントを削除します。

ページをApp Routerに移行する

/pagesディレクトリにある各ページについて、対応するフォルダ構造を/appディレクトリに作成する必要があります。

  • ページのURLパスと一致するフォルダ構造を/appに作成します。例えば、/pages/about.tsxにページがある場合、/app/about/page.tsxファイルを作成します。

  • page.tsxファイルに元のページコンポーネントの内容をコピーします。

  • ページコンポーネントがクライアントサイドの機能(Hooks、Browser APIなど)を使用する場合、ファイルの先頭にuse clientディレクティブを記述してラップする必要があります。

データフェッチをアップデートする

App Routerでは、Next.jsの従来のデータフェッチ方法(getStaticPropsgetServerSidePropsgetStaticPaths)は使用されません。その代わり、ページコンポーネント内で直接データをフェッチできます。

  • ページコンポーネント内にgetStaticPropsgetServerSidePropsgetStaticPathsのいずれかの関数がある場合は削除します。

  • JavaScript/TypeScriptの標準の非同期関数を使用し、ページコンポーネント内で直接データをフェッチします。

例1:データフェッチ

移行前(Pages Router)

// /pages/about.tsx
import { GetStaticProps } from 'next';

export const getStaticProps: GetStaticProps = async () => {
  const data = await fetchSomeData();
  return {
    props: { data },
  };
};

const AboutPage = ({ data }: { data: any }) => {
  return (
    <div>
      <h1>About Page</h1>
      <p>{data.message}</p>
    </div>
  );
};

export default AboutPage;

移行後(App Router)

import { fetchSomeData } from '@/lib/data';

const AboutPage = async () => {
  const data = await fetchSomeData();

  return (
    <div>
      <h1>About Page</h1>
      <p>{data.message}</p>
    </div>
  );
};

export default AboutPage;

ルーティングのHooksを移行する

App Routerでは、Pages Routerで使用されていたものに代わる新しいルーティングのHooksが導入されます。

  • next/routerからuseRouter()を使用する代わりに、next/navigationからuseRouter()usePathname()useSearchParams()を使用します。

  • 新しいHooksのuseRouter() は、pathnamequeryのプロパティを返しません。代わりに、usePathname()useSearchParams()を使用します。

例2:ルーティングのHooksの移行

移行前(Pages Router)

// /pages/users/[id].tsx
import { useRouter } from 'next/router';

const UserPage = () => {
  const { query } = useRouter();
  const userId = query.id as string;

  return (
    <div>
      <h1>User Page</h1>
      <p>User ID: {userId}</p>
    </div>
  );
};

export default UserPage;

移行後(App Router)

// /app/users/[id]/page.tsx
'use client';

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

const UserPage = () => {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const userId = searchParams.get('id');

  return (
    <div>
      <h1>User Page</h1>
      <p>User ID: {userId}</p>
    </div>
  );
}

export default UserPage;

エラーハンドリングをアップデートする

App Routerでは、Pages Routerとはエラーハンドリングのアプローチが異なります。

  • グローバルエラーのハンドリングについては、pages/_error.jsファイルをapp/error.tsxに置き換えます。

  • 特定のルートに関連するエラーの処理のためにページフォルダ内に個別のerror.tsxファイルを作成します。

例3:エラーハンドリングの移行

移行前(Pages Router)

// /pages/_error.js
import { NextPageContext } from 'next';

const ErrorPage = ({ statusCode }: { statusCode: number }) => {
  return (
    <div>
      <h1>Error {statusCode}</h1>
      <p>An error occurred on the server</p>
    </div>
  );
};

ErrorPage.getInitialProps = ({ res, err }: NextPageContext) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
  return { statusCode };
};

export default ErrorPage;

移行後(App Router)

// /app/error.tsx 
// エラーコンポーネントはClient Componentである必要があります!
'use client';

interface ErrorPageProps {
  error: Error & { digest?: string }
  reset: () => void;
}

const ErrorPage = ({ error, reset }: ErrorPageProps) => {
  return (
    <div>
      <h1>Error</h1>
      <p>{error.message}</p>
      <button onClick={
          // セグメントの再レンダリングにより復旧を試みます。
          () => reset()
        }>
		Try again
	  </button>
    </div>
  );
};

export default ErrorPage;

他の機能の移行

アプリケーションによっては、APIルートやミドルウェア、カスタムdocument/appコンポーネントなど他の機能も移行する必要があるかもしれません。これらの機能をApp Routerに移行する方法については、Next.jsのドキュメントをご覧ください。

テストと検証

移行が完了したら、アプリケーションのテストを行い、すべての機能が正常に動作することを確認しましょう。Pages RouterとApp Routerで挙動に違いが見られないか注意してみてください。

App RouterとPages Routerは同じNext.jsアプリケーション上に共存できるため、段階的な移行が可能です。App Routerがまだ対応していない特定のレガシー機能を残す必要がある場合などには、そうするとよいでしょう。

まとめ

Next.jsのPages Routerから新しいApp Routerに移行することで、アプリケーションのルーティング機能を強化し、柔軟性を高めることができます。依存関係をアップデートし、ファイル構造を再構築し、新しいデータフェッチとエラーハンドリングの方法に適応することで、App Routerの機能を最大限活用することができます。少し手間はかかりますが、アプリケーションの効率性と拡張性が向上することで明確なメリットが得られます。

監修者
監修者_古川陽介
古川陽介
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
複合機メーカー、ゲーム会社を経て、2016年に株式会社リクルートテクノロジーズ(現リクルート)入社。 現在はAPソリューショングループのマネジャーとしてアプリ基盤の改善や運用、各種開発支援ツールの開発、またテックリードとしてエンジニアチームの支援や育成までを担う。 2019年より株式会社ニジボックスを兼務し、室長としてエンジニア育成基盤の設計、技術指南も遂行。 Node.js 日本ユーザーグループの代表を務め、Node学園祭などを主宰。