メインコンテンツまでスキップ

Layouts と Templates

特別なファイルであるlayout.jstemplate.jsを使用すると、ルート間で共通のUIを作成できます。このページでは、これらの特別なファイルをいつ、どのように使用するかについて説明します。

Layouts

レイアウトは、複数のルート間で共有されるUIです。ナビゲーション時、レイアウトは状態を保持し、インタラクティブに保たれ、再レンダリングされません。レイアウトはネストすることもできます。

layout.jsファイルからReactコンポーネントをデフォルトエクスポートすることで、レイアウトを定義できます。このコンポーネントは、childレイアウト(存在する場合)やページで満たされるchildren propを受け取る必要があります。

たとえば、このレイアウトは/dashboardおよび/dashboard/settingsページと共有されます:

layout.js特別ファイルlayout.js特別ファイル
app/dashboard/layout.tsx
export default function DashboardLayout({
children, // ページまたはネストされたレイアウトになります
}: {
children: React.ReactNode
}) {
return (
<section>
{/* ここに共有UIを含めます。例として、ヘッダーやサイドバーがあります */}
<nav></nav>

{children}
</section>
)
}

Root レイアウト(必須)

Root レイアウトはappディレクトリのトップレベルで定義され、すべてのルートに適用されます。このレイアウトは必須であり、htmlbodyタグを含む必要があります。これにより、サーバーから返される初期HTMLを変更できます。

app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{/* レイアウトUI */}
<main>{children}</main>
</body>
</html>
)
}

ネストされたレイアウト

デフォルトでは、フォルダ階層内のレイアウトはネストされており、children propを介して子レイアウトをラップします。特定のルートセグメント(フォルダ)内にlayout.jsを追加することでレイアウトをネストできます。

たとえば、/dashboardルート用のレイアウトを作成するには、dashboardフォルダ内に新しいlayout.jsファイルを追加します:

ネストされたレイアウトネストされたレイアウト
app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}

上記の2つのレイアウトを組み合わせると、root レイアウト(app/layout.js)がダッシュボードレイアウト(app/dashboard/layout.js)をラップし、app/dashboard/*内のルートセグメントをラップします。

2つのレイアウトは次のようにネストされます:

ネストされたレイアウトネストされたレイアウト

Good to know:

  • Layoutsには.js.jsx、または.tsxファイル拡張子を使用できます。
  • <html>および<body>タグを含めることができるのはroot レイアウトのみです。
  • 同じフォルダにlayout.jspage.jsファイルが定義されている場合、レイアウトはページをラップします。
  • レイアウトはデフォルトでServer Componentsですが、Client Componentに設定することもできます。
  • レイアウトはデータを取得できます。詳細はデータ取得セクションを参照してください。
  • 親レイアウトからその子供へのデータの受け渡しはできません。ただし、同じルートでデータを複数回取得することができ、Reactがパフォーマンスに影響を与えずにリクエストを自動で重複排除します
  • レイアウトはpathnameにアクセスできません(詳細はこちら)。ただし、インポートされたClient ComponentsはusePathnameフックを使用してパス名にアクセスできます。
  • レイアウトはそれ自体より下のルートセグメントにアクセスできません。すべてのルートセグメントにアクセスするには、Client ComponentでuseSelectedLayoutSegmentまたはuseSelectedLayoutSegmentsを使用できます。
  • 共有レイアウトから特定のルートセグメントをオプトインまたはオプトアウトするには、Route Groupsを使用できます。
  • Route Groupsを使用して複数のroot レイアウトを作成できます。こちらの例をご覧ください
  • pagesディレクトリからの移行: root レイアウトは_app.jsおよび_document.jsファイルを置き換えます。移行ガイドをご覧ください

Templates

テンプレートは、レイアウトに似ていて、子レイアウトまたはページをラップします。ルート間を持続し状態を維持するレイアウトとは異なり、テンプレートはナビゲーション時に各子供の新しいインスタンスを作成します。つまり、ユーザーがテンプレートを共有するルート間をナビゲートすると、子供の新しいインスタンスがマウントされ、DOM要素が再生成され、Client Componentsの状態は保持されず、effectsは再同期されます。

特定の動作が必要な場合は、テンプレートのほうがレイアウトよりも適切な選択肢になることがあります。例としては:

  • ナビゲーション時にuseEffectを再同期するため
  • ナビゲーション時に子供のClient Componentsの状態をリセットするため

テンプレートは、template.jsファイルからReactコンポーネントをデフォルトエクスポートすることにより定義できます。このコンポーネントはchildren propを受け取る必要があります。

template.js特別ファイルtemplate.js特別ファイル
app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}

ネストに関しては、template.jsはレイアウトとその子の間でレンダリングされます。以下は簡略化した出力例です:

Output
<Layout>
{/* テンプレートにはユニークなキーが与えられます */}
<Template key={routeParam}>{children}</Template>
</Layout>

Examples

メタデータ

メタデータAPIを使用して、titlemetaのような<head> HTML要素を変更できます。

メタデータは、metadataオブジェクトまたはgenerateMetadata関数layout.jsまたはpage.jsファイルでエクスポートすることで定義できます。

app/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
title: 'Next.js',
}

export default function Page() {
return '...'
}

Good to know: <title>および<meta>のような<head>タグを手動でroot レイアウトに追加すべきではありません。代わりにメタデータAPIを使用してください。これにより、ストリーミングや<head>要素の重複排除などの高度な要件が自動的に処理されます。

利用可能なメタデータオプションについては、APIリファレンスをご覧ください。

usePathname()フックを使用して、ナビリンクがアクティブかどうかを判定できます。

usePathname()はクライアントフックですので、ナビリンクをClient Componentに抽出し、レイアウトまたはテンプレートにインポートする必要があります:

app/ui/nav-links.tsx
'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function NavLinks() {
const pathname = usePathname()

return (
<nav>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
ホーム
</Link>

<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
アバウト
</Link>
</nav>
)
}
app/layout.tsx
import { NavLinks } from '@/app/ui/nav-links'

export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
)
}