AppRouterのlayoutのグループ化
開発環境
- next 13.4.10
前提
Next.jsのv13.4からStableになったAppRouterを利用します。
また、本記事ではAppRouterのlayoutに焦点を当てています。
本題
AppRouterのlayoutを利用する際のポイント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つだと仮定します。
- loginセグメントではヘッダー、フッターを描画したくない
- 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と比較すると変わる部分が多く、実装方法も変わってくるなと思いました。
まだまだ実践で利用できていないので、この記事も今後アップデートするかもしれません。