🐱

Next13のレイアウトを試してみる

2022/10/27に公開

先日リリースされたNext.js v13のレイアウトを試してみます。

設定

以前はpages/**/*.tsxに配置していましたが、v13からはapp/**/page.tsxとして配置することで、親ルートのレイアウトからネストした状態のページを簡単に作成できるようになりました。

使用するためにはnext.config.jsの変更が必要です。
experimental.appDirをtrueに変更します。
また、pagesディレクトリは不要となるため、削除します

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  experimental: {
    appDir: true,
  },
}

module.exports = nextConfig

app以下の各種ファイル

  • page.tsx ... ページ固有のUIを定義ファイル。自動的に親ルートからのレイアウトと、現在のルートのレイアウト内にマウントされます。
  • layout.tsx ... レイアウトの定義ファイル
  • error.tsx ... エラーが発生した際に表示されるUIの定義ファイル。React Error Boundaryで自動的にWrapされるとのこと
  • template.tsx ... layoutと似ていますが、ページ遷移の度にアンマウントされ、状態がリセットされます
  • head.tsx ... ページのhead情報を設定します

今回はpage.tsxとlayout.tsxのみ使用します。

/appにlayout.tsxと、page.tsxを作成します。

/app/layout.tsx
import { FC, ReactNode } from 'react';

type Props = {
  children?: ReactNode;
}

const HomeLayout: FC<Props> = ({ children }) => {
  return (
    <div
      style={{
        padding: '20px',
        background: '#ddd',
      }}
    >
      {children}
    </div>
  );
};

export default HomeLayout;
/app/page.tsx
import { FC } from 'react';

const HomePage: FC = () => {
  return (
    <div>
      Hello!
    </div>
  );
};

export default HomePage;

devServerを起動し、localhost:3000にアクセスしてみます。

yarn dev


レイアウトが反映されていることがわかります。

/appに hogeというディレクトリを作成し、
/hoge/page.tsxを作成してみます。

/app/hoge/page.tsx
import { FC } from 'react';

const HogePage: FC = () => {
  return <div>Hoge!</div>
}

export default HogePage;

localhost:3000/hogeにアクセスしてみます。


親ルートのレイアウトが引き継げています!

ルートは違うが、同じレイアウトを使いたい

下層ページが親ページのレイアウトをネストできることはわかりましたが、ルートはそれぞれ違うけれど同じレイアウトを使いたいことがあると思います。
その場合は、ディレクトリ名を「()」で括った名前にすることで、共通のレイアウトを使用できます。

/appに(guest)というディレクトリを作成し、
直下にlayout.tsxを作成します。

/app/(guest)/layout.tsx
import { FC, ReactNode } from 'react';

type Props = {
  children?: ReactNode;
}

const GuestLayout: FC<Props> = ({ children }) => {
  return (
    <div
      style={{
        padding: '20px',
        background: '#daa',
      }}
    >
      {children}
    </div>
  );
};

export default GuestLayout;

/app/(guest)にloginと、guestという名前のディレクトリを作成します。  
それぞれのディレクトリにpage.tsxを作成します

/app/(guest)/login/page.tsx
import { FC } from 'react';

const LoginPage: FC = () => {
  return <div>this is login page.</div>
}

export default LoginPage;
/app/(guest)/register/page.tsx
import { FC } from 'react';

const RegisterPage: FC = () => {
  return <div>this is register page.</div>
}

export default RegisterPage;

localhost:3000/loginとlocalhost:3000/registerにアクセスしてみます。



(guest)で設定したレイアウトがそれぞれのルートで適用されています!

ダイナミックルート

[id]/page.tsxというような構成にすることで、ダイナミックルートを作成できます。
(idではなく、slugなど、他の単語でもOK)

/app/post/[id]に、page.tsxを作成してみます。

/app/post/[id]/page.tsx
import { FC } from 'react';

type Props = {
  params: { id: string };
};

const PostPage: FC<Props> = ({ params }) => {
  return <div>this is post {params.id} page.</div>;
};

export default PostPage;

ダイナミックルートでは、Propsとしてparamsを受け取ることができます。paramsの中には、[id]でアクセスした際のパスパラメータが入っています。
クエリパラメータを取得したい場合は、searchParamsというパラメータから参照することができます。

localhost:3000/post/1にアクセスしてみます。

指定されたパスパラメータでアクセスすることができました!

以上!

Discussion