Layouts and Templates
特別なファイルであるlayout.jsとtemplate.jsを使用すると、ルート間で共有されるUIを作成できます。このページでは、これらの特別なファイルをどのように、そしていつ使用するかを案内します。
Layouts
レイアウトは、複数のルート間で共有されるUIです。ナビゲーション時に、レイアウトは状態を保持し、インタラクティブなままで、再レンダリングされません。レイアウトはネストすることもできます。
layout.js
ファイルからReactコンポーネントをデフォルトエクスポートすることで、レイアウトを定義できます。コンポーネントは、レンダリング中に子レイアウト(存在する場合)またはページで埋められるchildren
プロップを受け入れる必要があります。
たとえば、レイアウトは/dashboard
および/dashboard/settings
ページと共有されます:
- TypeScript
- JavaScript
export default function DashboardLayout({
children, // ページまたはネストされたレイアウトになります
}: {
children: React.ReactNode
}) {
return (
<section>
{/* ここに共有UIを含めます。例:ヘッダーやサイドバー */}
<nav></nav>
{children}
</section>
)
}
export default function DashboardLayout({
children, // ページまたはネストされたレイアウトになります
}) {
return (
<section>
{/* ここに共有UIを含めます。例:ヘッダーやサイドバー */}
<nav></nav>
{children}
</section>
)
}
Root Layout (必須)
root レイアウトはapp
ディレクトリのトップレベルで定義され、すべてのルートに適用されます。このレイアウトは必須であり、html
およびbody
タグを含める必要があります。これにより、サーバーから返される初期HTMLを変更できます。
- TypeScript
- JavaScript
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{/* レイアウトUI */}
<main>{children}</main>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{/* レイアウトUI */}
<main>{children}</main>
</body>
</html>
)
}
ネストされたレイアウト
デフォルトでは、フォルダ階層内のレイアウトはネストされており、children
プロップを介して子レイアウトをラップします。特定のルートセグメント(フォルダ)内にlayout.js
を追加することで、レイアウトをネストできます。
たとえば、/dashboard
ルートのレイアウトを作成するには、dashboard
フォルダ内に新しいlayout.js
ファイルを追加します:
- TypeScript
- JavaScript
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
return <section>{children}</section>
}
上記の2つのレイアウトを組み合わせると、root レイアウト(app/layout.js
)がダッシュボードレイアウト(app/dashboard/layout.js
)をラップし、app/dashboard/*
内のルートセグメントをラップします。
2つのレイアウトは次のようにネストされます:
Good to know:
- レイアウトには
.js
、.jsx
、または.tsx
ファイル拡張子を使用できます。<html>
および<body>
タグを含めることができるのはroot レイアウトのみです。- 同じフォルダに
layout.js
とpage.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
プロップを受け入れる必要があります。
- TypeScript
- JavaScript
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
export default function Template({ children }) {
return <div>{children}</div>
}
ネストに関しては、template.js
はレイアウトとその子の間にレンダリングされます。以下は簡略化された出力です:
<Layout>
{/* テンプレートにはユニークなキーが与えられます。 */}
<Template key={routeParam}>{children}</Template>
</Layout>
例
メタデータ
Metadata APIsを使用して、title
やmeta
などの<head>
HTML要素を変更できます。
メタデータは、layout.js
またはpage.js
ファイルでmetadata
オブジェクトまたはgenerateMetadata
関数をエクスポートすることで定義できます。
- TypeScript
- JavaScript
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Page() {
return '...'
}
export const metadata = {
title: 'Next.js',
}
export default function Page() {
return '...'
}
Good to know: root レイアウトに
<title>
や<meta>
などの<head>
タグを手動で追加するべきではありません。代わりに、Metadata APIを使用してください。これにより、ストリーミングや<head>
要素の重複排除などの高度な要件が自動的に処理されます。
利用可能なメタデータオプションについては、APIリファレンスで詳細を確認してください。
アクティブなナビゲーションリンク
usePathname()フックを使用して、ナビゲーションリンクがアクティブかどうかを判断できます。
usePathname()
はクライアントフックであるため、ナビゲーションリンクをClient Componentに抽出し、レイアウトやテンプレートにインポートする必要があります:
- TypeScript
- JavaScript
'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>
)
}
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
export function Links() {
const pathname = usePathname()
return (
<nav>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</nav>
)
}
- TypeScript
- JavaScript
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>
)
}
import { NavLinks } from '@/app/ui/nav-links'
export default function Layout({ children }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
)
}