コンテンツにスキップ

layout.js

layout ファイルは、Next.js アプリケーションでレイアウトを定義するために使用されます。

app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

ルートレイアウトは、ルート app ディレクトリの一番上のレイアウトです。 <html> および <body> タグ、その他のグローバルに共有される UI を定義するために使用されます。

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

リファレンス

Props

children(必須)

レイアウトコンポーネントは children プロップを受け入れて使用する必要があります。レンダリング中、children はレイアウトがラップしているルートセグメントで展開されます。これらは主に子 レイアウト(存在する場合)または ページ のコンポーネントになりますが、該当する場合は ローディング または エラー のような他の特別なファイルになることもあります。

params (オプション)

ルートセグメントからそのレイアウトまでの 動的ルートパラメータ オブジェクトを含むオブジェクトに解決される Promise。

app/dashboard/[team]/layout.tsx
export default async function Layout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ team: string }>
}) {
  const { team } = await params
}
例:ルートURLparams
app/dashboard/[team]/layout.js/dashboard/1Promise<{ team: '1' }>
app/shop/[tag]/[item]/layout.js/shop/1/2Promise<{ tag: '1', item: '2' }>
app/blog/[...slug]/layout.js/blog/1/2Promise<{ slug: ['1', '2'] }>
  • params プロップは Promise であるため、値にアクセスするには async/await または React の use 関数を使用する必要があります。
    • バージョン 14 以前では、params は同期プロップでした。下位互換性を支援するために、Next.js 15 でも同期的にアクセスできますが、この動作は将来的に非推奨になります。

レイアウトプロップヘルパー

LayoutProps でレイアウトを型付けすることで、ディレクトリ構造から推論される強力な型を持つ params および名前付きスロットを取得できます。 LayoutProps はグローバルに利用可能なヘルパーです。

app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) {
  return (
    <section>
      {props.children}
      {/* If you have app/dashboard/@analytics, it appears as a typed slot: */}
      {/* {props.analytics} */}
    </section>
  )
}

知っておくと良いこと:

  • 型は next devnext build、または next typegen の実行中に生成されます。
  • 型生成後、LayoutProps ヘルパーはグローバルに利用可能になります。インポートする必要はありません。

ルートレイアウト

app ディレクトリには、ルート app ディレクトリの一番上のレイアウトであるルートレイアウト含める必要があります。通常、ルートレイアウトは app/layout.js です。

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}
  • ルートレイアウトは、<html> および <body> タグを定義する必要があります。
    • ルートレイアウトに <title><meta> のような <head> タグを手動で追加しないでください。代わりに、ストリーミングや <head> 要素の重複排除などの高度な要件を自動的に処理する メタデータAPI を使用する必要があります。
  • ルートグループを使用して、複数のルートレイアウトを作成できます。
    • 複数のルートレイアウト間のナビゲーションは、フルページロード(クライアントサイドナビゲーションとは対照的)を引き起こします。たとえば、app/(shop)/layout.js を使用する /cart から app/(marketing)/layout.js を使用する /blog にナビゲートすると、フルページロードが発生します。これは複数のルートレイアウトにのみ適用されます。
  • ルートレイアウトは動的セグメントの下に配置できます。たとえば、app/[lang]/layout.js を使用して国際化 国際化 を実装する場合などです。

注意点

リクエストオブジェクト

ナビゲーション中にレイアウトはクライアント側でキャッシュされるため、不要なサーバーリクエストが回避されます。

レイアウトは再レンダリングされません。ページ間のナビゲーション時に不要な計算を回避するためにキャッシュして再利用できます。レイアウトが生の要求にアクセスできないように制限することで、Next.js はレイアウト内で潜在的に遅い、またはコストのかかるユーザーコードの実行を防ぐことができ、パフォーマンスへの悪影響を軽減できます。

リクエストオブジェクトにアクセスするには、サーバーコンポーネントおよび関数で headers API および cookies API を使用できます。

app/shop/layout.tsx
import { cookies } from 'next/headers'
 
export default async function Layout({ children }) {
  const cookieStore = await cookies()
  const theme = cookieStore.get('theme')
  return '...'
}

クエリパラメータ

レイアウトはナビゲーション時に再レンダリングされないため、古い値になる可能性のある検索パラメータにアクセスできません。

更新されたクエリパラメータにアクセスするには、Page の searchParams プロップを使用するか、クライアントコンポーネント内で useSearchParams フックを使用して読み取ることができます。クライアントコンポーネントはナビゲーション時に再レンダリングされるため、最新のクエリパラメータにアクセスできます。

app/ui/search.tsx
'use client'
 
import { useSearchParams } from 'next/navigation'
 
export default function Search() {
  const searchParams = useSearchParams()
 
  const search = searchParams.get('search')
 
  return '...'
}
app/shop/layout.tsx
import Search from '@/app/ui/search'
 
export default function Layout({ children }) {
  return (
    <>
      <Search />
      {children}
    </>
  )
}

パス名

レイアウトはナビゲーション時に再レンダリングされないため、古い値になる可能性のあるパス名にアクセスできません。

現在のパス名にアクセスするには、クライアントコンポーネント内で usePathname フックを使用して読み取ることができます。クライアントコンポーネントはナビゲーション中に再レンダリングされるため、最新のパス名にアクセスできます。

app/ui/breadcrumbs.tsx
'use client'
 
import { usePathname } from 'next/navigation'
 
// Simplified breadcrumbs logic
export default function Breadcrumbs() {
  const pathname = usePathname()
  const segments = pathname.split('/')
 
  return (
    <nav>
      {segments.map((segment, index) => (
        <span key={index}>
          {' > '}
          {segment}
        </span>
      ))}
    </nav>
  )
}
app/docs/layout.tsx
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
 
export default function Layout({ children }) {
  return (
    <>
      <Breadcrumbs />
      <main>{children}</main>
    </>
  )
}

データ取得

レイアウトは children にデータを渡すことができません。ただし、ルートで同じデータを複数回取得し、React の cache を使用して、パフォーマンスに影響を与えることなくリクエストを重複排除できます。

または、Next.js で fetch を使用する場合、リクエストは自動的に重複排除されます。

app/lib/data.ts
export async function getUser(id: string) {
  const res = await fetch(`https://.../users/${id}`)
  return res.json()
}
app/dashboard/layout.tsx
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}
    </>
  )
}
app/dashboard/page.tsx
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>
  )
}

子セグメントへのアクセス

レイアウトは、その下のルートセグメントにアクセスできません。すべてのルートセグメントにアクセスするには、クライアントコンポーネントで useSelectedLayoutSegment または useSelectedLayoutSegments を使用できます。

app/ui/nav-link.tsx
'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}`}
      // Change style depending on whether the link is active
      style={{ fontWeight: isActive ? 'bold' : 'normal' }}
    >
      {children}
    </Link>
  )
}
app/blog/layout.tsx
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>
  )
}

Metadata

title および meta のような <head> HTML 要素は、metadata オブジェクト または generateMetadata 関数 を使用して変更できます。

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Next.js',
}
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return '...'
}

知っておくと良いこと: ルートレイアウトに <title><meta> のような <head> タグを手動で追加しないでください。代わりに、ストリーミングや <head> 要素の重複排除などの高度な要件を自動的に処理する メタデータAPI を使用してください。

ナビゲーションリンクがアクティブかどうかを判断するために usePathname フックを使用できます。

usePathname はクライアントフックであるため、ナビゲーションリンクをクライアントコンポーネントに抽出する必要があります。これはレイアウトにインポートできます。

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>
  )
}

params に基づくコンテンツの表示

dynamic route segments を使用して、params プロップに基づいて特定のコンテンツを表示または取得できます。

app/dashboard/layout.tsx
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>
  )
}

クライアントコンポーネントでの params の読み取り

クライアントコンポーネント(async にできない)で params を使用するには、Promise を読み取るために React の use 関数を使用できます。

app/page.tsx
'use client'
 
import { use } from 'react'
 
export default function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = use(params)
}

バージョン履歴

バージョン変更履歴
v15.0.0-RCparams は Promise になりました。 コードモッド が利用可能です。
v13.0.0layout が導入されました。