logo

AppRouterのlayoutのグループ化

2023-07-23
a year ago

開発環境

  • next 13.4.10

前提

Next.jsのv13.4からStableになったAppRouterを利用します。

また、本記事ではAppRouterlayoutに焦点を当てています。

本題

AppRouterlayoutを利用する際のポイント2つを紹介します。

以下の悩みがある人にはおすすめです。

  • 親で定義したレイアウトを子のセグメントで引き継ぎたくない
  • 無駄なファイルパスが増えてしまっている

ルーティングについて

Next.jsでは従来からフォルダ内のファイルパスでルーティングを決定する方式(file-system based router)を採用しています。

AppRouterでも同じ方式をとっていますが、色々とアップデートされています。

layoutとは

AppRouterのファイルシステムでは、入れ子になったルートで特定の動作を行う、 UI を作成するための特別なファイルがあります。

ファイル規則のページで紹介されているように、page, not-foundなどがあります。

その一つであるlayoutはセグメントとその子の共有 UIの役割を担います。要するに、セグメントでlayoutを定義すると、そのセグメントからネストしたセグメントにlayoutが継承されていきます。

また、ディレクトリの最上位で定義されるルートレイアウトは必須になります。

.
└── app
    ├── page.tsx
    ├── layout.tsx ← ルートレイアウト(ヘッダー、フッター付き)
    |
    ├── login
    |      └── page.tsx ← ルートレイアウトを継承しているので、ヘッダー、フッターがある
    |
    └── articles
           ├── page.tsx ← ルートレイアウトを継承し、ヘッダー、フッター、サイドバーがある
           └── layout.tsx ← サイドバーを実装

このように、下の階層のセグメントでは親のレイアウトを引き継ぎます。

自身で定義したレイアウトは親のレイアウトにラップされるので、articlesページの例ではヘッダー、フッター、サイドバーが描画されます。

課題

ここで、やりたいことが以下の2つだと仮定します。

  1. loginセグメントではヘッダー、フッターを描画したくない
  2. URLパスは最低限にしたい、分かりやすくしたい

ポイント① ルートレイアウトは最低限にする

AppRouterの仕組み上、親のレイアウトは引き継ぎます。

今回のケースでいうと、ルートレイアウトでヘッダー、フッターを実装しているのでどうしても引き継がれてしまいます。

なので、今回の場合ルートのレイアウトはどのページにも適応して良い最低限のレイアウトにするべきです。

ルートレイアウトのみに<html><body>タグを含めることができるので、それらだけにすることで、配下のセグメントにレイアウトは任せるようにします。

import type { Metadata } from 'next';
import type { ReactNode } from 'react';

export const metadata: Metadata = {
  title: 'title',
  description: 'description',
};

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
.
└── app
    ├── page.tsx
    ├── layout.tsx ← ルートレイアウト(最低限にする)
    |
    ├── login
    |     └── page.tsx ← ルートレイアウトを継承しているが、ヘッダー、フッターはない
    |
    └── articles
          ├── page.tsx ← ルートレイアウトを継承しているが、ヘッダー、フッターはない
          └── layout.tsx ← ヘッダー、フッター、サイドバーを実装

ポイント② ルーティングをグループ化する

ポイント①のやり方だけでも問題ないケースもあると思いますが、ほとんどの場合、 articlesでヘッダー、フッター、サイドバーを管理してしまうと実装しにくくなります。

なぜなら、新しく追加するページに関しても、articlesセグメントからネストさせることになるからです。

そこでRoute Groupsを活用します。

通常、appディレクトリ内では、ネストされたフォルダはURL パスにマップされます。ただし、フォルダをRoute Groupsとしてマークして、そのフォルダがルートの URL パスに含まれないようにすることができます。

URL パスが増えることを想定してグループ化する例です。

.
└── app
    ├── layout.tsx ← ルートレイアウト(最低限にする)
    |
    ├── (auth)
    |     ├── layout.tsx ← ルートレイアウトを継承 + authグループで共通のレイアウト
    |     ├── login
    |     |     └── page.tsx
    |     └── signup
    |           └── page.tsx
    |
    └── (public)
          ├── layout.tsx ← ルートレイアウトを継承 + publicグループで共通のレイアウト
          ├── articles
          |     └── page.tsx
          └── tags
                └── page.tsx

この例の(auth)と(public)はURLパスにマッピングされません。

例)実際のURLパス
x /auth/login
○ /login

URLパスは最低限にしつつ、共通のレイアウトをグループで共有することができました。

さいごに

layoutに焦点をあてましたが、AppRouterは従来のPageRouterと比較すると変わる部分が多く、実装方法も変わってくるなと思いました。

まだまだ実践で利用できていないので、この記事も今後アップデートするかもしれません。

参照