ページとレイアウト
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/1
、posts/2
などでアクセスできます。
動的ルーティングの詳細については、動的ルーティングのドキュメントを確認してください。
レイアウトパターン
Reactモデルでは、ページをコンポーネントのシリーズに分解できます。これらのコンポーネントの多くは、ページ間で再利用されることがよくあります。例えば、すべてのページで同じナビゲーションバーとフッターを持つことがあります。
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
例
カスタムAppを使用した単一の共有レイアウト
アプリケーション全体で1つのレイアウトしか使用しない場合、カスタムAppを作成し、レイアウトでアプリケーションをラップできます。<Layout />
コンポーネントはページ遷移時に再利用されるため、そのコンポーネントの状態は保持されます(例:入力値)。
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
ページごとのレイアウト
複数のレイアウトが必要な場合、ページにgetLayout
プロパティを追加して、レイアウトのReactコンポーネントを返すことができます。これにより、_ページごとに_レイアウトを定義できます。関数を返すため、必要に応じて複雑にネストされたレイアウトを作成できます。
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>
)
}
export default function MyApp({ Component, pageProps }) {
// ページレベルで定義されたレイアウトを使用。未定義の場合はページをそのまま返す
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
ページ間を移動する際、シングルページアプリケーション(SPA)の体験のために、ページの状態(入力値、スクロール位置など)を_維持_したいと考えます。
このレイアウトパターンは、ページ遷移間でReactコンポーネントツリーが維持されるため、状態の永続化を可能にします。コンポーネントツリーにより、Reactは状態を保持する要素を理解できます。
補足: このプロセスは調停と呼ばれ、Reactが変更された要素を理解する方法です。
TypeScriptの場合
TypeScriptを使用する場合、まずgetLayout
関数を含む新しいページ用の型を作成する必要があります。次に、以前に作成した型を使用してAppProps
のComponent
プロパティをオーバーライドする新しい型を作成します。
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
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) {
// ページレベルで定義されたレイアウトを使用。未定義の場合はページをそのまま返す
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
データフェッチ
レイアウト内で、useEffect
またはSWRのようなライブラリを使用してクライアント側でデータをフェッチできます。このファイルはページではないため、現時点ではgetStaticProps
またはgetServerSideProps
を使用できません。
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>読み込みに失敗しました</div>
if (!data) return <div>読み込み中...</div>
return (
<>
<Navbar links={data.links} />
<main>{children}</main>
<Footer />
</>
)
}