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 prop。在渲染过程中,children 将被填充为布局所包装的路由段。这些主要是子级布局 (如果存在) 或页面的组件,但在适用的情况下也可能是其他特殊文件,如加载错误

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 prop 是一个 promise,你必须使用 async/await 或 React 的 use 函数来访问这些值。
    • 在版本 14 及更早版本中,params 是一个同步 prop。为了向后兼容,你在 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>。相反,你应该使用元数据 API,它会自动处理流式传输和去重 <head> 元素等高级需求。
  • 你可以使用路由组来创建多个根布局。
    • 多个根布局之间导航将导致完整的页面加载 (而不是客户端导航)。例如,从使用 app/(shop)/layout.js/cart 导航到使用 app/(marketing)/layout.js/blog 将导致完整的页面加载。这适用于多个根布局。

注意事项

布局不接收 searchParams

页面不同,布局组件不会接收 searchParams prop。这是因为共享布局在导航期间不会重新渲染,这可能导致在导航之间出现过时的 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 不会重新渲染,布局中服务器组件的 searchParams prop 在导航后可能会过时

相反,可以使用页面的 searchParams prop 或在布局中的客户端组件使用 useSearchParams hook,它会在客户端使用最新的 searchParams 重新渲染。

布局无法访问 pathname

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

相反,你可以将依赖于 pathname 的逻辑提取到客户端组件中,并将其导入到你的布局中。由于客户端组件在导航期间会重新渲染 (但不会重新获取),你可以使用 Next.js 的 hook,如 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 prop 来实现。

查看示例部分了解更多信息。

示例

基于 params 显示内容

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

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

在客户端组件中读取 params

要在客户端组件 (不能是 async) 中使用 params,你可以使用 React 的 use 函数来读取 promise:

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

版本历史

版本变更
v15.0.0-RCparams 现在是一个 promise。提供了一个 codemod
v13.0.0引入 layout