Next.js の Parallel Routes に入門したらユーザー体験に関する悩みが良い意味で増えた話

はじめに

こんにちは。ソーシャル経済メディア「NewsPicks」の NewsPicks Stage. チームの三嶋です。 普段は、NewsPicks Stage. という経済情報に特化したオンライン番組配信プラットフォームの開発をしています。 NewsPicks Stage. チームでは、昨年末からフロントエンドのアプリケーションで Next.js の App Router を使い始めています。 今回は、Next.js の Parallel Routes を使った際の気づきを共有させてください。

Parallel Routes とは

詳細は公式を参照ください。

端的には、複数のページを1つのレイアウトに同時並行で描画できる機能だと理解しています。 基本的な実装方法としては route segments 配下に @video のような @ 始まりのディレクトリ(slot)を作り、その配下に page.tsx や layout.tsx などを実装していくことになります。

画面とディレクトリ構成

下記の画像は、NewsPicks Stage. で動画を視聴するための画面です。 主に、動画を再生するプレイヤー部分と、複数のタブから構成されるツールでできており、ツールは「チャット」「番組概要」「プロフィール編集」のいずれかにタブ切り替えできるようになっています。 また、ツールの「番組概要」では番組の出演者を一覧で見ることができるのですが、出演者をクリックするとその出演者の詳細を見ることができます。

この画面での Parallel Routes を使ったディレクトリ構成は下記のようになっています。

app/live-theaters/[id]
├── @player                # 動画プレイヤー
│
├── @tool                  # ツール
│   ├── chat               ## チャット
│   │
│   ├── description        ## 番組概要
│   │   ├── @guests        ### 出演者一覧
│   │   ├── @guestDetail   ### 出演者詳細
│   │   └── layout.tsx
│   │
│   ├── profile            ## プロフィール編集
│   │
│   ├── @chat
│   │
│   └── layout.tsx
│
├── @popup
│
└── layout.tsx

まず、単純な例として app/live-theaters/[id] 直下の @player と @tool の slot に注目します。この2つは Parallel Routes によって、/live-theaters/[id] のパスにアクセスした際に「動画プレイヤー」と「ツール」が同時並行で描画されるようにしています。※@player などの slot は route segments ではなく URL には現れてきません。

次に、@tool 配下に注目すると、chat、description、profile という route segments が切られており、/live-theaters/[id]/description/live-theaters/[id]/profile のようなパスへのアクセスによって、それぞれのパスに応じたページが描画されることになります。※なお、タブ切り替えでは Soft Navigation によって各パスへのアクセスを行っていますが、Partial Rendering によって @tool のサブページが部分的に再描画されることになります。

/live-theaters/[id] というページの中に、更に /description/profile というパスによってナビゲーションできるこの例を見ると、1つのレイアウトの中で複数ページが描画されるということがより実感できてくるかと思います。

さらに Parallel Routes

ツールの「番組概要」というタブでは、番組の出演者の方々を一覧で見ることができます。 また、出演者をクリックすることで出演者の詳細を見ることができるのですが、ここでもさらに Parallel Routes によって描画を最適化しています。

app/live-theaters/[id]
├── @player                # 動画プレイヤー
│
├── @tool                  # ツール
│   ├── chat               ## チャット
│   │
│   ├── description        ## 番組概要
│   │   ├── @guests        ### 出演者一覧
│   │   ├── @guestDetail   ### 出演者詳細
│   │   └── layout.tsx

app/live-theaters/[id]/description 直下に @guests と @guestDetail があり、この2つが Parallel Routes によって同時並行で描画されるようにしています。実際には、初期描画では @guests(出演者一覧)がユーザーから見えており、全出演者ぶんの出演者詳細(@guestDetail)はユーザーからは見えていないものの裏で描画されている状態になります(CSS で隠しています)。これにより、出演者詳細に切り替える前に既に出演者詳細の描画が完了しているため、ユーザーからみた時に一覧から詳細への切り替えがスムーズになるということが実現できています。尚且つ、出演者詳細は出演者一覧とは同時並行で描画されているため、出演者一覧の初期描画を遅くすることがありません。

個人的な気づき

Parallel Routes を利用したことで、slot に分割したそれぞれのページを独立して開発していけばよいため認知負荷が減って開発しやすくなりましたし、タブ切り替えなどの状態を持つ代わりにパスを変えることによって描画するものを切り替えることができるため、開発体験としても良かったかと思います。また、同時並行に描画されることを利用して出演者一覧と出演者詳細の切り替えをスムーズにするという実装は、ユーザー体験に関して考える良いきっかけになりました。

App Router を使い始める以前は Colocation による開発体験の向上にしか目が向いていませんでしたが、いざチーム内の Next.js に詳しいメンバーから話を聞いていると、まず出てくるのはユーザー体験の話でした。本当に Client Compoent にすべきものはなんなのか、Next.js を活かせる画面構成はどういうものなのか、いかにして初期描画の負荷を抑えるのか、など。

Next.js では他にも Server Actions や Cache などユーザー体験をよくするための機能があると思いますが、ユーザーに届けたい体験のために機能をどう利用するのかを改めて考えさせられた良い経験でした。 Component やロジックの名前を考えたり、今自分たちが何を作るべきなのかビジネスドメインへの理解を深めたり探究している時が楽しいなと感じますが、そういった考えるべきことに集中できるようにツールも選定していきたいなと改めて実感しました。

最後に

今回ご紹介した内容以外にも、チャットの WebSocket 接続を維持しながら番組概要を描画するために @chat という slot を用意していたり、slot 間の状態共有に Context を使うなど、Parallel Routes を使う際に工夫した点があります。それらについても別の機会でご紹介できればと思います。

Page top