Pages 和 Layouts
Pages
page 在路由中是独有的 UI。你可以通过从 page.js文件中导出组件的方式来定义 page 页面。
使用嵌套路由和 page.js文件才能使路由公开可访问。
在 app 文件夹中添加一个page.js文件。

tsx1// `app/page.tsx` is the UI for the `/` URL 2export default function Page() { 3 return <h1>Hello, Home page!</h1> 4}
tsx1// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL 2export default function Page() { 3 return <h1>Hello, Dashboard Page!</h1> 4}
page 是服务端组件,但是也可以被设置成 client 组件。
page 可以直接获取数据。
layouts
layouts 是在多个 pages 之间共享的 UI。在导航中,layouts 可以保存状态、交互并且不会重新渲染。layouts 可以被嵌套。
你可以通过在 layout.js中default导出一个 React 组件来定义 layout。
组件应接受一个 childrenprop,这样它的子组件就可以通过它渲染。

tsx1export default function DashboardLayout({ 2 children, // will be a page or nested layout 3}: { 4 children: React.ReactNode 5}) { 6 return ( 7 <section> 8 {/* Include shared UI here e.g. a header or sidebar */} 9 <nav></nav> 10 11 {children} 12 </section> 13 ) 14}
- 最顶级的 layout 被称为
Root layout,这个必须的 layout 可以共享给所有页面,Root layout 必须包含 html 和 body。- 所有的路由 segment 都可以定义它独有的 layout。这些 layout 在该 segment 中可以被所有页面共享。
- 默认的 layout 是嵌套的,每个父 layout 都通过
children包含它的子 layout。- 你可以使用 Route Group(路由分组)去操作特定的路由 segment,进入或者退出共享布局。
- 默认情况下,layout 是服务端组件。但可以设置成客户端组件。
- layout 可以获取数据。
- 父 layout 和子 layout 之间不能传递数据,但是你可以在一个路由中多次获取相同的数据,React 会自动去重请求,不影响性能。
- layout 不能访问当前的路由 segment,要访问 segment,可以在客户端组件中使用
useSelectedLayoutSegment或useSelectedLayoutSegments。- 同一个文件夹中定义 layout 和 page 文件,layout 将包裹 page。
Root Layout(必须)
root layout 是定义在 app 文件夹内最顶级的文件,它将和所有路由绑定。
这个 layout 使得修改从服务器返回的初始化 HTML 成为可能。
tsx1export default function RootLayout({ children }: { children: React.ReactNode }) { 2 return ( 3 <html lang="en"> 4 <body>{children}</body> 5 </html> 6 ) 7}
- app 文件夹必须包含 root layout
- root layout 必须定义
<html>和body标签。因为 Nextjs 不能自动创建他们。 - 你可以使用 built-in SEO support 来管理
<head>标签。 - 你可以使用路由分组去创建多个 root layout。example here.
- root layout 是服务端组件,不能被设置成客户端组件。
从 page 路由迁移过来的话:root layout 可以用来替换
_app.js和_document.js文件。
嵌套 layout
layout 在目录中定义,并绑定该目录所映射的特定的 segment,比如app/dashboard/layout.js用来做/dashboard这一 segment 的 UI。
默认情况下,layout 在文件目录结构中是嵌套的,这就意味着得通过children属性包裹它的子 layout。

tsx1export default function DashboardLayout({ children }: { children: React.ReactNode }) { 2 return <section>{children}</section> 3}
只有 root layout 才需要包含
<html>和<body>。
如果将上面的两个布局组合起来,根布局(app/layout.js)将包裹dashboard布局(app/dashboard/layout.js),而dashboard布局将包裹app/dashboard/*中的路由段。
两个 layout 将会这样被嵌套:

你可以使用路由分组来操作指定的 route segment 进入或者退出共享的 layout。
Templates
模板(Templates)与布局(Layouts)类似,它们都包裹每个子布局或页面。但与在整个路由中保持持久性和状态的 Layouts 不同,Templates 在导航时为其每个子组件创建一个新实例。这意味着当用户在共享 Templates 的路由之间导航时,组件的新实例将被挂载,DOM 元素将被重新创建,状态不会被保留,并且效果将重新同步。
在某些情况下,您可能需要这些特定的行为,而模板可能比布局更合适。例如:
- 使用 CSS 或动画库的进入/退出动画。
- 需要依赖 useEffect(例如记录页面访问)和 useState(例如每个页面的反馈表单)的功能。
- 修改默认的框架行为。例如,在 layout 内部的 Suspense 边界仅在第一次加载布局时显示回退内容,而在切换页面时则不显示。而对于 Templates,每次导航都会显示回退内容。
推荐使用 layout,除非你有特殊的原因才选择 Template。
可以通过从 template.js 文件导出默认的 React 组件来定义 template。该组件应该接受一个children属性,该 props 会将被嵌套的 segments 包裹住。

tsx1export default function Template({ children }: { children: React.ReactNode }) { 2 return <div>{children}</div> 3}
具有 layout 和 template 的路由片段将会被变成这个形式的输出:
tsx1<Layout> 2 {/* Note that the template is given a unique key. */} 3 <Template key={routeParam}>{children}</Template> 4</Layout>
修改<head>
在 app 目录中,你可以修改<head>元素,比如 title 和 meta 等以支持 SEO。
Meta 属性可以被在 layout.js或者page.hs文件中导出的 metadata object 或 generateMetadata定义。
tsx1import { Metadata } from 'next' 2 3export const metadata: Metadata = { 4 title: 'Next.js', 5} 6 7export default function Page() { 8 return '...' 9}
在处理注入<title>或者<meta>这类标签时,你不应该手动添加<head>标签。而是应该使用MetadataAPI 去自动处理更高级的需求比如streaming或者消除重复的<head>标签。