layout.js
layout
檔案用於在您的 Next.js 應用程式中定義版面配置。
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
return <section>{children}</section>
}
根版面配置 (root layout) 是根目錄 app
中最頂層的版面配置。它用於定義 <html>
和 <body>
標籤以及其他全域共享的 UI。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
參考
屬性
children
(必填)
版面配置元件應接受並使用 children
屬性。在渲染過程中,children
將被填充為該版面配置所包裹的路由區段。這些主要會是子 Layout (如果存在) 或 Page 的元件,但也可能是其他特殊檔案,如 Loading 或 Error (當適用時)。
params
(選填)
一個解析為包含 動態路由參數 物件的 Promise,從根區段向下到該版面配置。
export default async function Layout({
params,
}: {
params: Promise<{ team: string }>
}) {
const { team } = await params
}
export default async function Layout({ params }) {
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'] }> |
- 由於
params
屬性是一個 Promise,您必須使用async/await
或 React 的use
函數來存取其值。- 在版本 14 及更早版本中,
params
是一個同步屬性。為了向後兼容,您仍然可以在 Next.js 15 中同步存取它,但此行為將在未來被棄用。
- 在版本 14 及更早版本中,
根版面配置
app
目錄 必須 包含一個根 app/layout.js
。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>{children}</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html>
<body>{children}</body>
</html>
)
}
- 根版面配置 必須 定義
<html>
和<body>
標籤。- 您 不應 手動將
<head>
標籤(如<title>
和<meta>
)添加到根版面配置中。相反,您應該使用 Metadata API,它會自動處理進階需求,如流式傳輸和去重複<head>
元素。
- 您 不應 手動將
- 您可以使用 路由群組 (route groups) 來建立多個根版面配置。
- 在 多個根版面配置之間導航 會導致 完整頁面重新載入 (與客戶端導航相反)。例如,從使用
app/(shop)/layout.js
的/cart
導航到使用app/(marketing)/layout.js
的/blog
將導致完整頁面重新載入。這 僅 適用於多個根版面配置。
- 在 多個根版面配置之間導航 會導致 完整頁面重新載入 (與客戶端導航相反)。例如,從使用
注意事項
請求物件
版面配置在導航期間會被快取在客戶端,以避免不必要的伺服器請求。
版面配置 不會重新渲染。它們可以被快取和重複使用,以避免在頁面之間導航時進行不必要的計算。通過限制版面配置存取原始請求,Next.js 可以防止在版面配置中執行可能緩慢或昂貴的使用者代碼,這可能會對性能產生負面影響。
要存取請求物件,您可以在 伺服器元件 (Server Components) 和函數中使用 headers
和 cookies
API。
import { cookies } from 'next/headers'
export default async function Layout({ children }) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
import { cookies } from 'next/headers'
export default async function Layout({ children }) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
查詢參數
版面配置在導航時不會重新渲染,因此它們無法存取可能會過時的搜尋參數。
要存取更新的查詢參數,您可以使用 Page searchParams
屬性,或在客戶端元件中使用 useSearchParams
鉤子來讀取它們。由於客戶端元件在導航時會重新渲染,因此它們可以存取最新的查詢參數。
'use client'
import { useSearchParams } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
return '...'
}
'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}
</>
)
}
import Search from '@/app/ui/search'
export default function Layout({ children }) {
return (
<>
<Search />
{children}
</>
)
}
路徑名稱
版面配置在導航時不會重新渲染,因此它們無法存取可能會過時的路徑名稱。
要存取當前路徑名稱,您可以在客戶端元件中使用 usePathname
鉤子來讀取它。由於客戶端元件在導航時會重新渲染,因此它們可以存取最新的路徑名稱。
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
export default function Layout({ children }) {
return (
<>
<Breadcrumbs />
<main>{children}</main>
</>
)
}
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
export default function Layout({ children }) {
return (
<>
<Breadcrumbs />
<main>{children}</main>
</>
)
}
資料獲取
版面配置無法將資料傳遞給其 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 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>
)
}
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
。
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>
)
}
import { NavLink } from './nav-link'
import getPosts from './get-posts'
export default async function Layout({ children }) {
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
物件 或 generateMetadata
函數 來修改 <head>
HTML 元素,如 title
和 meta
。
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Layout({ children }: { children: React.ReactNode }) {
return '...'
}
export const metadata = {
title: 'Next.js',
}
export default function Layout({ children }) {
return '...'
}
須知:您 不應 手動將
<head>
標籤(如<title>
和<meta>
)添加到根版面配置中。相反,請使用 Metadata APIs,它會自動處理進階需求,如流式傳輸和去重複<head>
元素。
活動導航連結
您可以使用 usePathname
鉤子來確定導航連結是否處於活動狀態。
由於 usePathname
是一個客戶端鉤子,您需要將導航連結提取到客戶端元件中,然後可以將其導入到您的版面配置中:
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>
)
}
import { NavLinks } from '@/app/ui/nav-links'
export default function Layout({ children }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
)
}
根據 params
顯示內容
使用動態路由區段 (dynamic route segments),您可以根據 params
屬性顯示或取得特定內容。
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>
)
}
export default async function DashboardLayout({ children, params }) {
const { team } = await params
return (
<section>
<header>
<h1>歡迎來到 {team} 的儀表板</h1>
</header>
<main>{children}</main>
</section>
)
}
在客戶端元件 (Client Components) 中讀取 params
若要在客戶端元件 (無法使用 async
) 中使用 params
,可以使用 React 的 use
函式來讀取 promise:
'use client'
import { use } from 'react'
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params)
}
'use client'
import { use } from 'react'
export default function Page({ params }) {
const { slug } = use(params)
}
版本歷史
版本 | 變更內容 |
---|---|
v15.0.0-RC | params 現在是一個 promise。提供代碼修改工具 (codemod)。 |
v13.0.0 | 引入 layout 。 |