Sponsor
ntab.devntab.dev 提升效率的新标签页组件
点击查看
Menu

layout.js

layout 文件用于在你的 Next.js 应用中定义布局。

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

根布局是位于根 app 目录中的最顶层布局。它用于定义 <html><body> 标签以及其他全局共享的 UI。

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

参考

Props

children(必需)

布局组件应接受并使用 children 属性。在渲染期间,children 将被填充为布局所包裹的路由片段。这些主要是子级 Layout(如果存在)或 Page 的组件,但在适用的情况下也可能是其他特殊文件,如 LoadingError

params(可选)

一个 Promise,解析为包含从根片段到该布局的 动态路由参数 对象。

app/dashboard/[team]/layout.tsx
TypeScript
export default async function Layout({
  params,
}: {
  params: Promise<{ team: string }>
}) {
  const team = (await params).team
}
示例路由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 中仍然可以同步访问它,但这个行为将在未来被弃用。

根布局

app 目录必须包含一个根 app/layout.js

app/layout.tsx
TypeScript
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}
  • 根布局必须定义 <html><body> 标签。
    • 不应手动添加 <head> 标签,如 <title><meta> 到根布局中。相反,你应该使用 Metadata API,它会自动处理流式传输和去重 <head> 元素等高级需求。
  • 你可以使用路由组创建多个根布局。
    • 多个根布局之间导航将导致完整页面加载(而不是客户端导航)。例如,从使用 app/(shop)/layout.js/cart 导航到使用 app/(marketing)/layout.js/blog 将导致完整页面加载。这适用于多个根布局。

注意事项

布局不接收 searchParams

Pages 不同,布局组件接收 searchParams 属性。这是因为共享布局在导航期间不会重新渲染,这可能导致导航之间的 searchParams 过时。

使用客户端导航时,Next.js 自动只渲染两个路由之间共同布局以下的部分页面。

例如,在以下目录结构中,dashboard/layout.tsx/dashboard/settings/dashboard/analytics 两者的共同布局:

文件结构显示 dashboard 文件夹嵌套了 layout.tsx 文件,以及 settings 和 analytics 文件夹与它们各自的页面

当从 /dashboard/settings 导航到 /dashboard/analytics 时,/dashboard/analytics 中的 page.tsx 将在服务器上重新渲染,而 dashboard/layout.tsx不会重新渲染,因为它是两个路由之间共享的 UI。

这种性能优化使得共享布局的页面之间的导航更快,因为只需运行页面的数据获取和渲染,而不是整个路由,包括可能获取自己数据的共享布局。

因为 dashboard/layout.tsx 不重新渲染,布局中的 Server Component 的 searchParams 属性可能在导航后变得过时

相反,使用页面的 searchParams 属性或布局中的 Client Component 中的 useSearchParams 钩子,它将在客户端用最新的 searchParams 重新渲染。

布局无法访问 pathname

布局无法访问 pathname。这是因为布局默认是 Server Components,并且在客户端导航期间不重新渲染,这可能导致导航之间的 pathname 变得过时。为了防止过时,Next.js 需要重新获取路由的所有片段,这会失去缓存的好处并增加导航时的 RSC payload 大小。

相反,你可以将依赖于 pathname 的逻辑提取到 Client Component 中,并将其导入到布局中。由于 Client Components 在导航期间重新渲染(但不重新获取),你可以使用 Next.js 钩子,如 usePathname 来访问当前 pathname 并防止过时。

app/dashboard/layout.tsx
TypeScript
import { ClientComponent } from '@/app/ui/ClientComponent'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <ClientComponent />
      {/* 其他布局 UI */}
      <main>{children}</main>
    </>
  )
}

常见的 pathname 模式也可以用 params 属性实现。

更多信息请参见示例部分。

示例

基于 params 显示内容

使用动态路由片段,你可以基于 params 属性显示或获取特定内容。

app/dashboard/layout.tsx
TypeScript
export default async function DashboardLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ team: string }>
}) {
  const { team } = await params
 
  return (
    <section>
      <header>
        <h1>欢迎来到 {team} 的仪表板</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}

在 Client Components 中读取 params

要在 Client Component(不能是 async)中使用 params,你可以使用 React 的 use 函数来读取 promise:

app/page.tsx
TypeScript
'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.0引入 layout