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

Layouts and Templates

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

Layouts

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

layout.jsファイルからReactコンポーネントをデフォルトエクスポートすることで、レイアウトを定義できます。コンポーネントは、レンダリング中に子レイアウト(存在する場合)またはページで埋められるchildrenプロップを受け入れる必要があります。

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

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

{children}
</section>
)
}

Root Layout (必須)

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

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

ネストされたレイアウト

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

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

Nested LayoutNested Layout
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つのレイアウトは次のようにネストされます:

Nested LayoutsNested Layouts

Good to know:

  • レイアウトには.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の状態は保持されず、エフェクトが再同期されます。

これらの特定の動作が必要な場合があり、テンプレートはレイアウトよりも適したオプションとなるでしょう。たとえば:

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

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

template.js special filetemplate.js special file
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>

メタデータ

Metadata APIsを使用して、titlemetaなどの<head> HTML要素を変更できます。

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

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

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

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

Good to know: root レイアウトに<title><meta>などの<head>タグを手動で追加するべきではありません。代わりに、Metadata 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="/">
Home
</Link>

<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
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>
)
}