コンテンツへスキップ

ページとレイアウト

ページルーターは、ページの概念に基づいて構築されたファイルシステムベースのルーターです。

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

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

**例**: 以下の様なReactコンポーネントをエクスポートする`pages/about.js`を作成すると、`/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/1`、`posts/2`などでアクセスできるようになります。

動的ルーティングの詳細については、動的ルーティングのドキュメントを参照してください。

レイアウトパターン

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

components/layout.js
import Navbar from './navbar'
import Footer from './footer'
 
export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

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

アプリケーション全体で1つのレイアウトしか使用しない場合は、カスタムアプリを作成し、レイアウトでアプリケーションをラップできます。<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 (
    /** Your content */
  )
}
 
Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // Use the layout defined at the page level, if available
  const getLayout = Component.getLayout ?? ((page) => page)
 
  return getLayout(<Component {...pageProps} />)
}

ページ間を移動する際、シングルページアプリケーション(SPA)エクスペリエンスのために、ページの状態(入力値、スクロール位置など)を保持したいと考えています。

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

参考情報:このプロセスはリコンシリエーションと呼ばれ、Reactがどの要素が変更されたかを理解する方法です。

TypeScriptを使用する場合

TypeScriptを使用する場合は、最初にgetLayout関数を含むページの新しい型を作成する必要があります。次に、Componentプロパティを前に作成した型を使用するようにオーバーライドするAppPropsの新しい型を作成する必要があります。

pages/index.tsx
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
pages/_app.tsx
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
 
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode
}
 
type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}
 
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  // Use the layout defined at the page level, if available
  const getLayout = Component.getLayout ?? ((page) => page)
 
  return getLayout(<Component {...pageProps} />)
}

データフェッチング

レイアウト内では、useEffectまたはSWRのようなライブラリを使用して、クライアント側でデータをフェッチできます。このファイルはページではないため、現在getStaticPropsまたはgetServerSidePropsを使用することはできません。

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 />
    </>
  )
}