Next.jsの新しいルーティングシステムとNested Layoutsの実装について調べてみた

2022.06.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。

Next.jsで、新しいルーティングシステムについての記事が公式から出ました。

今回は中でも、ようやく提案されたNested Layoutsについて調べたので、まだ正式に実装されたわけではないですが、せっかくなので記事にしました。

Appフォルダの提供

Next.jsでは、pagesフォルダの配下に作成したファイルが、そのままURLのパスとして機能します。

新しいルーティングシステムでは、pagesフォルダではなく、機能追加に対して段階的に変更できるよう、新しくappフォルダを提供する考えのようです。そのため、appフォルダとpagesフォルダは併用できそうです。アップデートによる修正や更新の負担が減るので、これはありがたいです。もちろんpagesフォルダ同様、階層を下げてパスを指定できます。

pagesフォルダと違う点は、appフォルダではファイルではなく、フォルダが一つのパスとして機能するようです。これによる利点は、フォルダの配下にファイルを作成したとしても、そのファイルは一つのページとして認識されない点にあります。そのため、一つのページでしか使用しないUIコンポーネントのファイルや、hooksのファイルをフォルダ配下におけるようになります。

pageExtensions

pagesフォルダでも、pageExtensions機能を使用することで、実装が可能です。

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  // 「.bar.tsx」、「.foo.tsx」のファイルがページとして認識される
  pageExtensions: ["bar.tsx", "foo.tsx"]
}

module.exports = nextConfig

上記の例だと、拡張子が「.bar.tsx」と「.foo.tsx」のファイルのみページとして認識され、「index.tsx」のようなファイルはパスとして機能しなくなります。

Nested Layouts

NestedLayoutsは、共通部分をまとめたレイアウトを階層ごとに作成することで、その配下のページで複数のレイアウトを適用することができます。Remixでは初期の頃からこの仕様を提供しており、Next.jsとの大きな比較部分でした。

getLayout

一応、Next.jsでレイアウトの分岐を行うには、getLayout()を使用してページごとにレイアウトを指定する方法がありました。

_app.tsx

import "../styles/globals.css";
import type { AppProps } from "next/app";
import { NextPage } from "next";
import { ReactElement, ReactNode } from "react";

type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout =
    Component.getLayout ||
    ((page) => {
      return page;
    });

  return getLayout(<Component {...pageProps} />);
}

export default MyApp;

index.tsx

const Sample = () => {

  return (
    <>
      ...
    </>
  );
}

export default Sample;

// LayoutAを適用
Sample.getLayout = LayoutA;

Layouts

レイアウトでは、ファイル内で共通で使うUIを提供します。レイアウトを作成した階層(サブツリー)内のセグメント間で共有できます。レイアウトはパスに影響を与えず、ユーザーがページ間を移動しても再レンダリングされないです。

layout.jsファイルを作成し、コンポーネント内をラッピングすることで機能します。

import { Header } from "layouts/header/header";
import { Footer } from "layouts/footer/footer";

import type { ReactChild } from "react";

export const Layout = ({ children }: {children: ReactChild}) => {

  return (
    <>
      <Header />
      <main>
        {children}
      </main>
      <Footer/>
    </>
  );
};

Sample.tsx

import { Layout } from "../layouts/layouts";

const Sample = () => {

  return (
    <Layout>
      ...
    </Layout>
  );
};

export default Sample;

レイアウトは2つのタイプを提供します。

  • Root layout
  • Regular layout

2つ以上のレイアウトを入れ子にすることで、Nested Layouts機能を実装できます。

Root layout

ルートレイアウトは、すべてのページに共通するUI、設定を記述します。_appフォルダの直下に、layout.jsを作成することでルートレイアウトとして機能します。これは、_app.jsと_document.jsの大替として提供されるようです。また、、タブのカスタマイズもできます。

Regular layout

特定のフォルダ内に、layout.jsを作成することで通常のレイアウトとして機能します。一部のページにのみ、共通化させるレイアウトを実装したい場合に使用します。

ルートレイアウト、レギュラーレイアウトを組み合わせたり、レギュラーレイアウトを複数作成することで、ネストされたレイアウトを実装することができます。

まとめ

今回は、Nested Layoutsについて、調べてみました。今後の意向が容易になると思うので、これから新規にNext.jsを使用する場合は、pageExtensionsを設定しておくといいのかなと思いました。今後、どのように機能が追加されていくのか楽しみです。

ではまた。