layout.js
layoutファイルはNext.jsアプリケーションでレイアウトを定義するために使用されます。
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}ルートレイアウトはルートappディレクトリ内の最上位のレイアウトです。<html>と<body>タグおよびグローバルに共有されるUIを定義するために使用されます。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}Reference
Props
children(必須)
レイアウトコンポーネントはchildrenpropを受け入れて使用する必要があります。レンダリング中、childrenにはレイアウトがラップしているルートセグメントが入力されます。これは主に子Layout(存在する場合)またはPageのコンポーネントになりますが、該当する場合はLoadingまたはErrorなどの他の特殊ファイルであることもあります。
params(オプション)
ルートセグメントからそのレイアウトまでの動的ルートパラメータオブジェクトを含むオブジェクトに解決されるpromiseです。
export default async function Layout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
}| ルート例 | URL | params |
|---|---|---|
app/dashboard/[team]/layout.js | /dashboard/1 | Promise<{ team: '1' }> |
app/shop/[tag]/[item]/layout.js | /shop/1/2 | Promise<{ tag: '1', item: '2' }> |
app/blog/[...slug]/layout.js | /blog/1/2 | Promise<{ slug: ['1', '2'] }> |
paramspropはpromiseであるため、値にアクセスするにはasync/awaitまたはReactのuse関数を使用する必要があります。- バージョン14以前では、
paramsは同期的なpropでした。後方互換性をサポートするため、Next.js 15では同期的にアクセスすることもできますが、この動作は将来非推奨になります。
- バージョン14以前では、
Layout Props Helper
LayoutPropsを使用してレイアウトに型を付けて、ディレクトリ構造から推測された強い型のparamsと名前付きスロットを取得できます。LayoutPropsはグローバルに利用可能なhelperです。
export default function Layout(props: LayoutProps<'/dashboard'>) {
return (
<section>
{props.children}
{/* app/dashboard/@analyticsがある場合、型指定されたスロットとして表示されます: */}
{/* {props.analytics} */}
</section>
)
}補足:
- 型は
next dev、next buildまたはnext typegen実行時に生成されます。- 型生成後、
LayoutPropshelperはグローバルで利用可能になります。インポートする必要はありません。
Root Layout
appディレクトリにはルートレイアウトが含まれる必要があります。ルートレイアウトはルートappディレクトリ内の最上位のレイアウトです。通常、ルートレイアウトはapp/layout.jsです。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>{children}</body>
</html>
)
}- ルートレイアウトは**
<html>と<body>タグを定義する必要があります**。- ルートレイアウトに
<title>や<meta>などの<head>タグを手動で追加しないでください。代わりに、Metadata APIを使用してください。これにより、ストリーミングと<head>要素の重複排除などの高度な要件が自動的に処理されます。
- ルートレイアウトに
- ルートグループを使用して複数のルートレイアウトを作成できます。
- 複数のルートレイアウト間でナビゲートすると、ページ全体がリロードされます(クライアント側のナビゲーションではなく)。例えば、
app/(shop)/layout.jsを使用する/cartからapp/(marketing)/layout.jsを使用する/blogにナビゲートすると、ページ全体がリロードされます。これは複数のルートレイアウトにのみ適用されます。
- 複数のルートレイアウト間でナビゲートすると、ページ全体がリロードされます(クライアント側のナビゲーションではなく)。例えば、
- ルートレイアウトは動的セグメントの下に配置できます。例えば、
app/[lang]/layout.jsで国際化を実装する場合です。
Caveats
Request Object
レイアウトはナビゲーション中にクライアント内にキャッシュされ、不要なサーバーリクエストを回避します。
Layoutsは再レンダリングされません。キャッシュされて再利用され、ページ間をナビゲートする際の不要な計算を避けます。レイアウトがraw requestオブジェクトへのアクセスを制限することで、Next.jsはレイアウト内で実行される潜在的に遅いまたは高コストのユーザーコードの実行を防ぎ、パフォーマンスに悪影響を与えることを避けることができます。
requestオブジェクトにアクセスするには、Server Componentsおよび関数内でheadersとcookiesAPIを使用できます。
import { cookies } from 'next/headers'
export default async function Layout({ children }) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}Query params
レイアウトはナビゲーション時に再レンダリングされないため、古くなってしまう検索パラメータにアクセスできません。
更新されたクエリパラメータにアクセスするには、PagesearchParamspropを使用するか、useSearchParamshookを使用してClient Component内で読み込むことができます。Client Componentsはナビゲーション時に再レンダリングされるため、最新のクエリパラメータにアクセスできます。
'use client'
import { useSearchParams } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
return '...'
}import Search from '@/app/ui/search'
export default function Layout({ children }) {
return (
<>
<Search />
{children}
</>
)
}Pathname
レイアウトはナビゲーション時に再レンダリングされないため、古くなってしまうpathnameにアクセスできません。
現在のpathnameにアクセスするには、usePathnamehookを使用してClient Component内で読み込むことができます。Client Componentsはナビゲーション時に再レンダリングされるため、最新のpathnameにアクセスできます。
'use client'
import { usePathname } from 'next/navigation'
// 簡略版のbreadcrumbsロジック
export default function Breadcrumbs() {
const pathname = usePathname()
const segments = pathname.split('/')
return (
<nav>
{segments.map((segment, index) => (
<span key={index}>
{' > '}
{segment}
</span>
))}
</nav>
)
}import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
export default function Layout({ children }) {
return (
<>
<Breadcrumbs />
<main>{children}</main>
</>
)
}Fetching Data
レイアウトはデータをchildrenに渡すことができません。ただし、ルート内で同じデータを複数回フェッチして、Reactのcacheを使用してパフォーマンスに影響を与えずにリクエストを重複排除できます。
または、Next.jsでfetchを使用する場合、リクエストは自動的に重複排除されます。
export async function getUser(id: string) {
const res = await fetch(`https://.../users/${id}`)
return res.json()
}import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Layout({ children }) {
const user = await getUser('1')
return (
<>
<nav>
{/* ... */}
<UserName user={user.name} />
</nav>
{children}
</>
)
}import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Page() {
const user = await getUser('1')
return (
<div>
<h1>Welcome {user.name}</h1>
</div>
)
}Accessing child segments
レイアウトはそのすぐ下のルートセグメントにアクセスできません。すべてのルートセグメントにアクセスするには、Client ComponentでuseSelectedLayoutSegmentまたはuseSelectedLayoutSegmentsを使用できます。
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function NavLink({
slug,
children,
}: {
slug: string
children: React.ReactNode
}) {
const segment = useSelectedLayoutSegment()
const isActive = slug === segment
return (
<Link
href={`/blog/${slug}`}
// リンクがアクティブかどうかに応じてスタイルを変更
style={{ fontWeight: isActive ? 'bold' : 'normal' }}
>
{children}
</Link>
)
}import { NavLink } from './nav-link'
import getPosts from './get-posts'
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
const featuredPosts = await getPosts()
return (
<div>
{featuredPosts.map((post) => (
<div key={post.id}>
<NavLink slug={post.slug}>{post.title}</NavLink>
</div>
))}
<div>{children}</div>
</div>
)
}Examples
Metadata
<head> HTMLエレメント(titleやmetaなど)をmetadataオブジェクトまたはgenerateMetadata関数を使用して変更できます。
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Layout({ children }: { children: React.ReactNode }) {
return '...'
}補足:ルートレイアウトに
<title>や<meta>などの<head>タグを手動で追加しないでください。代わりに、Metadata APIを使用してください。これによりストリーミングと<head>要素の重複排除などの高度な要件が自動的に処理されます。
Active Nav Links
usePathnamehookを使用して、navリンクがアクティブかどうかを判定できます。
usePathnameはクライアントhookであるため、navリンクをClient Componentに抽出し、レイアウトにインポートする必要があります:
'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>
)
}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>
)
}Displaying content based on params
動的ルートセグメントを使用して、paramspropに基づいて特定のコンテンツを表示またはフェッチできます。
export default async function DashboardLayout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
return (
<section>
<header>
<h1>Welcome to {team}'s Dashboard</h1>
</header>
<main>{children}</main>
</section>
)
}Reading params in Client Components
Client Component内でparamsを使用するには(asyncにはできません)、React のuse関数を使用してpromiseを読み取ります:
'use client'
import { use } from 'react'
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params)
}Version History
| バージョン | 変更内容 |
|---|---|
v15.0.0-RC | paramsがpromiseになりました。codemodが利用可能です。 |
v13.0.0 | 導入時期:layout。 |