Next.jsのIntercepting Routesを使ってみた
こんにちは、hanetsukiです。
今回は、Next.js v13.3のAppRouterで利用できるようになっているIntercepting Routesを使って、モーダルと詳細画面を別々に作成できるようになりました。
本記事では、実際に作成したサイトを元にIntercepting RoutesとParallel Routesについてお話しします。
今回作ったもの
Interceptiong RoutesとParallel Routesの試験用に、簡単なWebサイトを作成しました。
リポジトリも公開しているので、詳細な実装が気になる方はそちらをご覧ください。
主な利用技術
- Node.js(v18.18.2)
- Next.js(v14.0.2)
- React(v18.2.0)
スクリーンショット画面
TOP画面 https://midjourney.tsuki-lab.net/
詳細モーダル
詳細画面 https://midjourney.tsuki-lab.net/image/4w6z707v7l7
Intercepting Routes
そもそもInterceptとはどういう意味でしょうか?
ggrと、下記のような解説がありました。
途中で捕らえる、横取りする、傍受する、さえぎる、迎撃する、要撃する、途中で奪う、インターセプトする、(…を)2 点間にはさみ取る
Weblio辞書より
利用方法・解説
Intercepting Routesを利用する場合は、フォルダの先頭に(..)
をつけます。
相対パス../
と同じ感覚です。
また、(...)
を使用すると appディレクトリからの相対パスとなります。
詳しくは、Intercepting Routes#Conventionをご覧ください。
よくある遷移フローとして、図のようなものがあります。
一覧画面のコンテンツを押下することで、詳細画面へ遷移
この遷移フローを下の図のようにインターセプトします。
本来遷移するはずの詳細画面には到達せず、別のpage.tsxを表示する。
Intercepting Routesは、このように遷移を傍受するような動作をします。
そして、本来遷移したかった詳細画面へは、直リンクで遷移することができます。
直接遷移して詳細画面を表示する
Parallel Routes
今回作成した詳細モーダルを実装するためには、Parallel Routesの履修もしないと行けません。
これも、Next.js 13.3からAppRouterで利用できるようになった機能です。
さて、parallelとはどういう意味でしょうか?
ggrと、下記のような解説がありました。
平行の、(…と)平行して、(事柄など)相等しい、相似する、並行する、(…と)相似して、一致して、対応して、並列の
Weblio辞書より
パラレルワールド(並行世界)とはよく言ったものです。
Parallel Routesはその名の通り、並行するルート挙動を実現させます。
利用方法・解説
Parallel Routes利用する場合は、@modal
のようにプレフィックスに@
をつけたフォルダを作成することで利用できます。
この@modal
より配下がapp/layout.tsx
内で参照されるchildren
とは別に、名前付きスロットmodal
として扱えるようになります。
Parallel Routesのイメージ図
export default function Layout(props: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
<>
{props.children}
{props.modal} {/* 名前付きスロットhogehoge */}
</>
)
}
今回は、@modalというルートを作成しました
実装
今回作成したWebサイトでは、下記の要素を満たす必要があります。
- ページ遷移時にURL付きのモーダルで画像の詳細を開くことができる。(Parallel Routes)
- 詳細画面はモーダルとは別に存在する。(Intercepting Routes)
そのため、二つの要素を組み合わせる必要あります。
私の環境での実際のファイル構造は下記のようになりました。
トピックとして幾つかのファイルを列挙します。
app/layout.tsx
Parallel Routesの設定を施します。
app/layout.tsx
export default function RootLayout({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<h1>...</h1>
{children}
{modal}
</body>
</html>
);
}
app/@modal/(.)image/[contentId]/page.tsx
詳細モーダルの設定をします。/image/[contentId]
のルートをインターセプトし、且つパラレルルートに表示できるように、ファイルのパスを設定します。
app/@modal/(.)image/[contentId]/page.tsx
const ImageDetailPage = async () => {
const data = await fetch(...);
return (
<Modal>
<Image {...data} />
</Modal>
);
};
export default ImageDetailPage;
app/@modal/default.tsx
/
の時にmodalスロットのパラレルルートが404にならないように設定します。
app/@modal/default.tsx
export default function Default() {
return null;
}
app/image/[contentId]/page.tsx
/image/[contentId]
でリロードした際に設定するための詳細ページを作成します。
app/image/[contentId]/page.tsx
const ImageDetailPage = async () => {
const data = await fetch(...);
return (
<main>
<div>
<div>detail information</div>
<Image />
</div>
<Link href="/">back to top</Link>
</main>
);
};
export default ImageDetailPage;
おわりに
今回はIntercepting RoutesとParallel Routesを使った実装をしてみました。
実際に作成してみて・触ってみて思った点が2点。
- ユーザーのページ回遊を妨げないUXを作成できる
- インターセプト状態と通常ページとで同等機能を用意する必要がある(?)
ECの商品ページやサービスのログイン・サインインページなどで活きそうだなと感じました。
ニーズが合いそうな機会があったら提案してみたいものですね!
それでは今回はこれにて。
参考
ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion
tree-to-image 使ってくれている!ありがとうございます🙌
デモサイト綺麗ですね!
(Next.js 14 の不安定さ起因な気がしますが初回ルートアクセス時、不規則にクラッシュしてるかもです(Safari, iOS, Mac))
余談ですが挿絵は Figma でしょうか?(矢印など綺麗だったので...)
tree-to-image 大変心地よく利用させていただきました!
(クラッシュ報告ありがとうございます。なかなか安定してくれないですね...少し様子見しておきたいと思います)
こちらはFigJamを利用しています!
Figmaでも良いのですが、カードや矢印をプリセットで扱えるので図示する際には非常に便利ですー
FigJam ありがとうございます!使ってみます! 🙌