🐩

Next.jsのParallel Routeの挙動を理解する

2024/09/04に公開

はじめに

Next.jsのApp RouterではParalell Routesという機能が利用できます。

https://nextjs.org/docs/app/building-your-application/routing/parallel-routes

機能の説明については公式ページで詳しく解説されていますが、実際にページ遷移した際にどのコンポーネントがレンダリングされるか等、自分は曖昧にしか理解できていませんでした。
そこで、今回はParalell Routesによってどのコンポーネントがレンダリングされているか確認するためのアプリを作って、挙動を確かめてみることにしました。

前提

Paralell Routesの機能自体については、公式ページにて説明されていますが、準備も含めて、簡単にここでおさらいしておきます。今回は以下のような構成のアプリを想定します。

// app/以下のファイル構成
├── layout.tsx
├── default.tsx
├── page.tsx
├── @left
│     ├── default.tsx
│     ├── page.tsx
│     └── groups
│            └── page.tsx
├── @right
│     ├── default.tsx
│     ├── page.tsx
│     └── users
│            └── page.tsx

Parallel Routesでは、接頭に@がつくSlotと呼ばれるディレクトリを用意し、layout.tsxにおいてslotを任意の位置にrederします(Slotはディレクトリですが、ルート扱いされません)。

type Props = {
  children: React.ReactNode;
  left: React.ReactNode;
  right: React.ReactNode;
}

const Layout: FC<Props> = ({ children, left, right }) => {
  return (
    <html lang="en">
      <body className={inter.className}>
        {children}
        {left}
        {right}
      </body>
    </html>
  );
}

このように配置することで、同じlayout内で、3つのルート/, /groups, /usersに対して、それぞれ状況に応じた **/defaults.tsx, **/page.tsxがrenderされることになります。

(ややこしく曖昧な表現になっていますが、以降の章で解説します🙇)。

検証

Parallel Routesは、状況に応じてどのファイルがrenderされるのか、このパターンが非常に混乱しやすいと自分は思います。
そのため今回は、現在のパスとrenderされているファイル、そのrender結果を同時に表示できるような検証用アプリを用意しました。

https://parallelroute-sample-ypin.vercel.app/

以降では、それぞれのルートに遷移した際にどのファイルがrenderされるのかを、このアプリで見ていきたいと思います。

/を開いた時

まず、ユーザーが/に訪れた際に表示されるページです。

それぞれの**/page.tsxが表示されていることがわかると思います。最もシンプルなケースですね。

/groupsに遷移した時

次に、/から/groupsに遷移してみます。

すると、/@left/page.tsx/@left/groups/page.tsxに変わりました。こちらは想定通りですが、他のファイルについては遷移前から変わりません。パスが/groupsなのにも関わらず、/page.tsxがrenderされているのは少し違和感があります。

この挙動については、公式のページに記載があります。

Soft Navigation: During client-side navigation, Next.js will perform a partial render, changing the subpage within the slot, while maintaining the other slot's active subpages, even if they don't match the current URL.

こちらによると、/から/groupsの遷移では、Soft Navigationが行われ、この場合/page.tsxなど、遷移後のパスとマッチしない場合であっても、Slot内のページは維持されるようです。

/page.tsxもLayout.tsxから見たとき、children=Slotの一つとなります。

/groupsをリロードした時

次に、現在のページである/groupsをリロードしてみます。

すると今度は、/page.tsx/@right/page.tsxがそれぞれ/default.tsx/@right/default.tsxに変わりました。こちらの挙動についても公式に記載があります。

Hard Navigation: After a full-page load (browser refresh), Next.js cannot determine the active state for the slots that don't match the current URL. Instead, it will render a default.js file for the unmatched slots, or 404 if default.js doesn't exist.

/groupsをリロードした場合、Hard Navigationが行われます。
この場合、リロード前に表示されていた/page.tsxなどの現在のルートとはマッチしないサブページでは、default.tsxがrenderされる挙動となるようです(defaults.tsxがない場合404となる)。

/groupsから/usersに遷移した時

最後に、/groupsから/usersに遷移してみます。

すると、/default.tsx/@left/groups/page.tsxはそのままですが、/@right/default.tsx/@right/users/page.tsxに変化しました。今回はSoft Navigationであるため、/groupsに遷移した時と同じパターンの遷移になっているかと思います。なんとなく、わかってきましたね。

まとめ

今回作成したアプリで、Parallel Routesによる以下の挙動が確認できました。

  • Soft Navigationによって遷移した場合、一度訪れたSlot内のサブページは維持される
  • ページリロードなどでHard Navigationが行われると、パスにマッチしないルートはdefault.tsxが表示される(defaults.tsxがない場合404となる)

このParallel Routesは公式ページにもある通り、ダッシュボードやModalで、遷移済みのサブページを維持しつつ別のサブページを表示したい時などに便利な機能になるかと思います。
(※ModalではIntercepting Rotesと組み合わせて使う必要があります)

最後に

ここまで読んでいただきありがとうございます🙇
実際に動く環境とコードはそれぞれ以下で公開しています。

動作環境:https://parallelroute-sample-ypin.vercel.app/
コード:https://github.com/mktu/parallelroute-sample

Discussion