2025年4月24日
Next.jsのPages RouterからApp Routerに移行する


(2024-7-10)by 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の従来のデータフェッチ方法(getStaticProps
、getServerSideProps
、getStaticPaths
)は使用されません。その代わり、ページコンポーネント内で直接データをフェッチできます。
-
ページコンポーネント内に
getStaticProps
、getServerSideProps
、getStaticPaths
のいずれかの関数がある場合は削除します。 -
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()
は、pathname
とquery
のプロパティを返しません。代わりに、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 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa