ページとレイアウト

Pages Router は、ページの概念に基づいたファイルシステムベースのルーターを提供します。

pages ディレクトリにファイルを追加すると、自動的にルートとして利用可能になります。

Next.js では、ページとは pages ディレクトリ内の .js.jsx.ts、または .tsx ファイルからエクスポートされる React コンポーネント です。各ページはファイル名に基づいてルートに関連付けられます。

: pages/about.js を作成し、以下のように React コンポーネントをエクスポートすると、/about でアクセス可能になります。

export default function About() {
  return <div>About</div>
}

インデックスルート

ルーターは index という名前のファイルをディレクトリのルートに自動的にルーティングします。

  • pages/index.js/
  • pages/blog/index.js/blog

ネストされたルート

ルーターはネストされたファイルをサポートしています。ネストされたフォルダ構造を作成すると、ファイルは同じ方法で自動的にルーティングされます。

  • pages/blog/first-post.js/blog/first-post
  • pages/dashboard/settings/username.js/dashboard/settings/username

ダイナミックルートを持つページ

Next.js はダイナミックルートを持つページをサポートしています。例えば、pages/posts/[id].js というファイルを作成すると、posts/1posts/2 などでアクセス可能になります。

ダイナミックルーティングについて詳しくは、ダイナミックルーティングのドキュメント を参照してください。

レイアウトパターン

React モデルでは、ページ を一連のコンポーネントに分解できます。これらのコンポーネントの多くは、ページ間で再利用されることがよくあります。例えば、すべてのページで同じナビゲーションバーとフッターを使用する場合があります。

components/layout.js
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

カスタム App を使用した単一の共有レイアウト

アプリケーション全体で1つのレイアウトしかない場合は、カスタム App を作成し、アプリケーションをレイアウトでラップできます。<Layout /> コンポーネントはページ変更時にも再利用されるため、コンポーネントの状態(入力値など)が保持されます。

pages/_app.js
import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

ページごとのレイアウト

複数のレイアウトが必要な場合は、ページに getLayout プロパティを追加し、レイアウト用の React コンポーネントを返すことができます。これにより、ページごとに レイアウトを定義できます。関数を返すため、必要に応じて複雑なネストされたレイアウトを作成できます。

pages/index.js

import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return (
    /** コンテンツ */
  )
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // ページレベルで定義されたレイアウトを使用(利用可能な場合)
  const getLayout = Component.getLayout ?? ((page) => page)

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

ページ間を移動する際、シングルページアプリケーション(SPA)のような体験のために、ページの状態(入力値、スクロール位置など)を 保持 したい場合があります。

このレイアウトパターンでは、ページ遷移間で React コンポーネントツリーが維持されるため、状態の永続化が可能です。コンポーネントツリーにより、React はどの要素が変更されたかを理解し、状態を保持できます。

豆知識: このプロセスは reconciliation(調和)と呼ばれ、React がどの要素が変更されたかを理解する方法です。

TypeScript を使用する場合

TypeScript を使用する場合、まず getLayout 関数を含む新しいページタイプを作成する必要があります。次に、Component プロパティをオーバーライドして、先ほど作成したタイプを使用する新しい AppProps タイプを作成する必要があります。

import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'

const Page: NextPageWithLayout = () => {
  return <p>hello world</p>
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}

export default Page

データフェッチング

レイアウト内では、useEffectSWR のようなライブラリを使用してクライアントサイドでデータを取得できます。このファイルは ページ ではないため、現在のところ getStaticPropsgetServerSideProps は使用できません。

components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  const { data, error } = useSWR('/api/navigation', fetcher)

  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>

  return (
    <>
      <Navbar links={data.links} />
      <main>{children}</main>
      <Footer />
    </>
  )
}

On this page