generateMetadata

您可以使用 metadata 物件或 generateMetadata 函數來定義元數據。

metadata 物件

要定義靜態元數據,可以從 layout.jspage.js 檔案匯出一個 Metadata 物件

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}

完整支援的選項清單請參閱 元數據欄位

generateMetadata 函數

依賴於動態資訊的元數據,例如當前路由參數、外部數據或父區段的 metadata,可以透過匯出一個返回 Metadata 物件generateMetadata 函數來設定。

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: Promise<{ id: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // 讀取路由參數
  const { id } = await params

  // 獲取數據
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // 可選地存取並擴展 (而非替換) 父元數據
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}

須知事項:

  • 元數據可以添加到 layout.jspage.js 檔案中。
  • Next.js 會自動解析元數據,並為頁面建立相關的 <head> 標籤。
  • metadata 物件和 generateMetadata 函數匯出僅在伺服器元件 (Server Components) 中支援。
  • 您不能從同一個路由區段同時匯出 metadata 物件和 generateMetadata 函數。
  • generateMetadata 中的 fetch 請求會自動記憶化 (memoized),以便在 generateMetadatagenerateStaticParams、Layouts、Pages 和 Server Components 之間共享相同數據。
  • 如果無法使用 fetch,可以使用 React 的 cache 函數
  • 基於檔案的元數據 具有更高優先級,會覆蓋 metadata 物件和 generateMetadata 函數。

參考

參數

generateMetadata 函數接受以下參數:

  • props - 包含當前路由參數的物件:

    • params - 包含從根區段到呼叫 generateMetadata 的區段的動態路由參數物件。例如:

      路由URLparams
      app/shop/[slug]/page.js/shop/1{ slug: '1' }
      app/shop/[tag]/[item]/page.js/shop/1/2{ tag: '1', item: '2' }
      app/shop/[...slug]/page.js/shop/1/2{ slug: ['1', '2'] }
    • searchParams - 包含當前 URL 的查詢參數 (search params) 的物件。例如:

      URLsearchParams
      /shop?a=1{ a: '1' }
      /shop?a=1&b=2{ a: '1', b: '2' }
      /shop?a=1&a=2{ a: ['1', '2'] }
  • parent - 父路由區段解析後的元數據的 Promise。

返回值

generateMetadata 應返回包含一個或多個元數據欄位的 Metadata 物件

須知事項:

  • 如果元數據不依賴於運行時資訊,應使用靜態的 metadata 物件 而非 generateMetadata 來定義。
  • fetch 請求會自動記憶化 (memoized),以便在 generateMetadatagenerateStaticParams、Layouts、Pages 和 Server Components 之間共享相同數據。如果無法使用 fetch,可以使用 React 的 cache 函數
  • searchParams 僅在 page.js 區段中可用。
  • Next.js 的 redirect()notFound() 方法也可以在 generateMetadata 中使用。

元數據欄位

支援以下欄位:

title

title 屬性用於設定文件的標題。可以定義為簡單的字串或可選的模板物件

字串
layout.js | page.js
export const metadata = {
  title: 'Next.js',
}
<head> 輸出
<title>Next.js</title>
default

title.default 可用於為未定義 title 的子路由區段提供後備標題

app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    default: 'Acme',
  },
}
app/about/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {}

// 輸出: <title>Acme</title>
template

title.template 可用於為路由區段中定義的 titles 添加前綴或後綴。

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
    default: 'Acme', // 建立模板時需要提供預設值
  },
}

須知事項:

  • title.template 應用於路由區段,而非定義它的區段。這意味著:

    • 添加 title.template 時,必須提供 title.default
    • layout.js 中定義的 title.template 不會應用於同一路由區段的 page.js 中定義的 title
    • page.js 中定義的 title.template 無效,因為頁面始終是路由的終止區段(它沒有任何子路由區段)。
  • 如果路由未定義 titletitle.default,則 title.template 無效

absolute

title.absolute 可用於提供忽略父區段中設定的 title.template 的標題。

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
  },
}

須知事項:

  • layout.js

    • title (字串) 和 title.default 為未定義自己 title 的子區段定義預設標題。如果存在,它將擴展來自最近父區段的 title.template
    • title.absolute 為子區段定義預設標題。它忽略來自父區段的 title.template
    • title.template 為子區段定義新的標題模板。
  • page.js

    • 如果頁面未定義自己的標題,將使用最近父區段的解析標題。
    • title (字串) 定義路由的標題。如果存在,它將擴展來自最近父區段的 title.template
    • title.absolute 定義路由標題。它忽略來自父區段的 title.template
    • title.templatepage.js 中無效,因為頁面始終是路由的終止區段。

description

layout.js | page.js
export const metadata = {
  description: 'The React Framework for the Web',
}
<head> 輸出
<meta name="description" content="The React Framework for the Web" />

其他欄位

layout.js | page.js
export const metadata = {
  generator: 'Next.js',
  applicationName: 'Next.js',
  referrer: 'origin-when-cross-origin',
  keywords: ['Next.js', 'React', 'JavaScript'],
  authors: [{ name: 'Seb' }, { name: 'Josh', url: 'https://nextjs.org' }],
  creator: 'Jiachi Liu',
  publisher: 'Sebastian Markbåge',
  formatDetection: {
    email: false,
    address: false,
    telephone: false,
  },
}
<head> 輸出
<meta name="application-name" content="Next.js" />
<meta name="author" content="Seb" />
<link rel="author" href="https://nextjs.org" />
<meta name="author" content="Josh" />
<meta name="generator" content="Next.js" />
<meta name="keywords" content="Next.js,React,JavaScript" />
<meta name="referrer" content="origin-when-cross-origin" />
<meta name="color-scheme" content="dark" />
<meta name="creator" content="Jiachi Liu" />
<meta name="publisher" content="Sebastian Markbåge" />
<meta name="format-detection" content="telephone=no, address=no, email=no" />

metadataBase

metadataBase 是一個便利選項,用於為需要完整 URL 的 metadata 欄位設定基礎 URL 前綴。

  • metadataBase 允許當前路由區段及以下定義的基於 URL 的 metadata 欄位使用相對路徑,而非原本需要的絕對 URL。
  • 欄位的相對路徑將與 metadataBase 組合形成完整的 URL。
layout.js | page.js
export const metadata = {
  metadataBase: new URL('https://acme.com'),
  alternates: {
    canonical: '/',
    languages: {
      'en-US': '/en-US',
      'de-DE': '/de-DE',
    },
  },
  openGraph: {
    images: '/og-image.png',
  },
}
<head> 輸出
<link rel="canonical" href="https://acme.com" />
<link rel="alternate" hreflang="en-US" href="https://acme.com/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://acme.com/de-DE" />
<meta property="og:image" content="https://acme.com/og-image.png" />

須知事項:

  • metadataBase 通常設定在根目錄的 app/layout.js 中,以應用於所有路由的基於 URL 的 metadata 欄位。
  • 所有需要絕對 URL 的基於 URL 的 metadata 欄位都可以透過 metadataBase 選項進行設定。
  • metadataBase 可以包含子網域,例如 https://app.acme.com 或基礎路徑,例如 https://acme.com/start/from/here
  • 如果 metadata 欄位提供了絕對 URL,則 metadataBase 將被忽略。
  • 在未設定 metadataBase 的情況下在基於 URL 的 metadata 欄位中使用相對路徑會導致建置錯誤。
  • Next.js 會標準化 metadataBase(例如 https://acme.com/)和相對欄位(例如 /path)之間的重複斜線為單一斜線(例如 https://acme.com/path)。

URL 組合

URL 組合優先考慮開發者意圖而非預設的目錄遍歷語義。

  • metadataBasemetadata 欄位之間的尾部斜線會被標準化。
  • metadata 欄位中的「絕對」路徑(通常會替換整個 URL 路徑)會被視為「相對」路徑(從 metadataBase 的末尾開始)。

例如,給定以下 metadataBase

import type { Metadata } from 'next'

export const metadata: Metadata = {
  metadataBase: new URL('https://acme.com'),
}

任何繼承上述 metadataBase 並設定自己值的 metadata 欄位將按以下方式解析:

metadata 欄位解析後的 URL
/https://acme.com
./https://acme.com
paymentshttps://acme.com/payments
/paymentshttps://acme.com/payments
./paymentshttps://acme.com/payments
../paymentshttps://acme.com/payments
https://beta.acme.com/paymentshttps://beta.acme.com/payments

openGraph

layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    url: 'https://nextjs.org',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.org/og.png', // 必須是絕對 URL
        width: 800,
        height: 600,
      },
      {
        url: 'https://nextjs.org/og-alt.png', // 必須是絕對 URL
        width: 1800,
        height: 1600,
        alt: '我的自訂替代文字',
      },
    ],
    videos: [
      {
        url: 'https://nextjs.org/video.mp4', // 必須是絕對 URL
        width: 800,
        height: 600,
      },
    ],
    audio: [
      {
        url: 'https://nextjs.org/audio.mp3', // 必須是絕對 URL
      },
    ],
    locale: 'en_US',
    type: 'website',
  },
}
<head> output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="https://nextjs.org/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://nextjs.org/og-alt.png" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="我的自訂替代文字" />
<meta property="og:video" content="https://nextjs.org/video.mp4" />
<meta property="og:video:width" content="800" />
<meta property="og:video:height" content="600" />
<meta property="og:audio" content="https://nextjs.org/audio.mp3" />
<meta property="og:type" content="website" />
layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    type: 'article',
    publishedTime: '2023-01-01T00:00:00.000Z',
    authors: ['Seb', 'Josh'],
  },
}
<head> output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2023-01-01T00:00:00.000Z" />
<meta property="article:author" content="Seb" />
<meta property="article:author" content="Josh" />

須知事項:

  • 對於 Open Graph 圖片,使用 檔案式 Metadata API 可能更方便。檔案式 API 會自動為你生成正確的中繼資料,而不需要手動同步設定檔與實際檔案。

robots

layout.tsx | page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  robots: {
    index: true,
    follow: true,
    nocache: false,
    googleBot: {
      index: true,
      follow: true,
      noimageindex: false,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
}
<head> output
<meta name="robots" content="index, follow" />
<meta
  name="googlebot"
  content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
/>

icons

須知事項:建議盡可能使用 檔案式 Metadata API 來設定圖示。檔案式 API 會自動生成正確的中繼資料,而不需要手動同步設定檔與實際檔案。

layout.js | page.js
export const metadata = {
  icons: {
    icon: '/icon.png',
    shortcut: '/shortcut-icon.png',
    apple: '/apple-icon.png',
    other: {
      rel: 'apple-touch-icon-precomposed',
      url: '/apple-touch-icon-precomposed.png',
    },
  },
}
<head> output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
layout.js | page.js
export const metadata = {
  icons: {
    icon: [
      { url: '/icon.png' },
      new URL('/icon.png', 'https://example.com'),
      { url: '/icon-dark.png', media: '(prefers-color-scheme: dark)' },
    ],
    shortcut: ['/shortcut-icon.png'],
    apple: [
      { url: '/apple-icon.png' },
      { url: '/apple-icon-x3.png', sizes: '180x180', type: 'image/png' },
    ],
    other: [
      {
        rel: 'apple-touch-icon-precomposed',
        url: '/apple-touch-icon-precomposed.png',
      },
    ],
  },
}
<head> output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="icon" href="https://example.com/icon.png" />
<link rel="icon" href="/icon-dark.png" media="(prefers-color-scheme: dark)" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
<link
  rel="apple-touch-icon"
  href="/apple-icon-x3.png"
  sizes="180x180"
  type="image/png"
/>

須知事項:Chromium 版本的 Microsoft Edge 已不再支援 msapplication-* 元標籤,因此不再需要這些標籤。

themeColor

已棄用:從 Next.js 14 開始,metadata 中的 themeColor 選項已被棄用。請改用 viewport 設定

colorScheme

已棄用:從 Next.js 14 開始,metadata 中的 colorScheme 選項已被棄用。請改用 viewport 設定

manifest

網頁應用程式清單 (Web Application Manifest),定義於 網頁應用程式清單規範

layout.js | page.js
export const metadata = {
  manifest: 'https://nextjs.org/manifest.json',
}
<head> output
<link rel="manifest" href="https://nextjs.org/manifest.json" />

twitter

Twitter 規範(令人驚訝地)不僅僅用於 X(原 Twitter)。

了解更多關於 Twitter 卡片標記參考

layout.js | page.js
export const metadata = {
  twitter: {
    card: 'summary_large_image',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: ['https://nextjs.org/og.png'], // 必須是絕對 URL
  },
}
<head> output
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
layout.js | page.js
export const metadata = {
  twitter: {
    card: 'app',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: {
      url: 'https://nextjs.org/og.png',
      alt: 'Next.js Logo',
    },
    app: {
      name: 'twitter_app',
      id: {
        iphone: 'twitter_app://iphone',
        ipad: 'twitter_app://ipad',
        googleplay: 'twitter_app://googleplay',
      },
      url: {
        iphone: 'https://iphone_url',
        ipad: 'https://ipad_url',
      },
    },
  },
}
<head> output
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:card" content="app" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
<meta name="twitter:image:alt" content="Next.js Logo" />
<meta name="twitter:app:name:iphone" content="twitter_app" />
<meta name="twitter:app:id:iphone" content="twitter_app://iphone" />
<meta name="twitter:app:id:ipad" content="twitter_app://ipad" />
<meta name="twitter:app:id:googleplay" content="twitter_app://googleplay" />
<meta name="twitter:app:url:iphone" content="https://iphone_url" />
<meta name="twitter:app:url:ipad" content="https://ipad_url" />
<meta name="twitter:app:name:ipad" content="twitter_app" />
<meta name="twitter:app:name:googleplay" content="twitter_app" />

viewport

已棄用:從 Next.js 14 開始,metadata 中的 viewport 選項已被棄用。請改用 viewport 設定

verification

layout.js | page.js
export const metadata = {
  verification: {
    google: 'google',
    yandex: 'yandex',
    yahoo: 'yahoo',
    other: {
      me: ['my-email', 'my-link'],
    },
  },
}
<head> output
<meta name="google-site-verification" content="google" />
<meta name="y_key" content="yahoo" />
<meta name="yandex-verification" content="yandex" />
<meta name="me" content="my-email" />
<meta name="me" content="my-link" />

appleWebApp

layout.js | page.js
export const metadata = {
  itunes: {
    appId: 'myAppStoreID',
    appArgument: 'myAppArgument',
  },
  appleWebApp: {
    title: 'Apple Web App',
    statusBarStyle: 'black-translucent',
    startupImage: [
      '/assets/startup/apple-touch-startup-image-768x1004.png',
      {
        url: '/assets/startup/apple-touch-startup-image-1536x2008.png',
        media: '(device-width: 768px) and (device-height: 1024px)',
      },
    ],
  },
}
<head> output
<meta
  name="apple-itunes-app"
  content="app-id=myAppStoreID, app-argument=myAppArgument"
/>
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Apple Web App" />
<link
  href="/assets/startup/apple-touch-startup-image-768x1004.png"
  rel="apple-touch-startup-image"
/>
<link
  href="/assets/startup/apple-touch-startup-image-1536x2008.png"
  media="(device-width: 768px) and (device-height: 1024px)"
  rel="apple-touch-startup-image"
/>
<meta
  name="apple-mobile-web-app-status-bar-style"
  content="black-translucent"
/>

alternates

layout.js | page.js
export const metadata = {
  alternates: {
    canonical: 'https://nextjs.org',
    languages: {
      'en-US': 'https://nextjs.org/en-US',
      'de-DE': 'https://nextjs.org/de-DE',
    },
    media: {
      'only screen and (max-width: 600px)': 'https://nextjs.org/mobile',
    },
    types: {
      'application/rss+xml': 'https://nextjs.org/rss',
    },
  },
}
<head> output
<link rel="canonical" href="https://nextjs.org" />
<link rel="alternate" hreflang="en-US" href="https://nextjs.org/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://nextjs.org/de-DE" />
<link
  rel="alternate"
  media="only screen and (max-width: 600px)"
  href="https://nextjs.org/mobile"
/>
<link
  rel="alternate"
  type="application/rss+xml"
  href="https://nextjs.org/rss"
/>
layout.js | page.js
export const metadata = {
  appLinks: {
    ios: {
      url: 'https://nextjs.org/ios',
      app_store_id: 'app_store_id',
    },
    android: {
      package: 'com.example.android/package',
      app_name: 'app_name_android',
    },
    web: {
      url: 'https://nextjs.org/web',
      should_fallback: true,
    },
  },
}
<head> output
<meta property="al:ios:url" content="https://nextjs.org/ios" />
<meta property="al:ios:app_store_id" content="app_store_id" />
<meta property="al:android:package" content="com.example.android/package" />
<meta property="al:android:app_name" content="app_name_android" />
<meta property="al:web:url" content="https://nextjs.org/web" />
<meta property="al:web:should_fallback" content="true" />

archives

描述具有歷史價值的記錄、文件或其他材料的集合(來源)。

layout.js | page.js
export const metadata = {
  archives: ['https://nextjs.org/13'],
}
<head> output
<link rel="archives" href="https://nextjs.org/13" />

assets

layout.js | page.js
export const metadata = {
  assets: ['https://nextjs.org/assets'],
}
<head> output
<link rel="assets" href="https://nextjs.org/assets" />

bookmarks

layout.js | page.js
export const metadata = {
  bookmarks: ['https://nextjs.org/13'],
}
<head> output
<link rel="bookmarks" href="https://nextjs.org/13" />

category

layout.js | page.js
export const metadata = {
  category: 'technology',
}
<head> output
<meta name="category" content="technology" />

facebook

你可以將 Facebook 應用程式或 Facebook 帳戶連接到你的網頁,以使用某些 Facebook 社交插件 Facebook 文件

須知事項:你可以指定 appId 或 admins,但不能同時指定兩者。

layout.js | page.js
export const metadata = {
  facebook: {
    appId: '12345678',
  },
}
<head> output
<meta property="fb:app_id" content="12345678" />
layout.js | page.js
export const metadata = {
  facebook: {
    admins: '12345678',
  },
}
<head> output
<meta property="fb:admins" content="12345678" />

如果你想生成多個 fb:admins 元標籤,可以使用陣列值。

layout.js | page.js
export const metadata = {
  facebook: {
    admins: ['12345678', '87654321'],
  },
}
<head> output
<meta property="fb:admins" content="12345678" />
<meta property="fb:admins" content="87654321" />

pinterest

你可以在網頁上啟用或停用 Pinterest Rich Pins

layout.js | page.js
export const metadata = {
  pinterest: {
    richPin: true,
  },
}
<head> output
<meta name="pinterest-rich-pin" content="true" />

other

所有中繼資料選項都應使用內建支援來涵蓋。然而,可能會有特定於你的網站的自訂中繼資料標籤,或是剛發布的全新中繼資料標籤。你可以使用 other 選項來渲染任何自訂中繼資料標籤。

layout.js | page.js
export const metadata = {
  other: {
    custom: 'meta',
  },
}
<head> output
<meta name="custom" content="meta" />

如果你想生成多個相同鍵的元標籤,可以使用陣列值。

layout.js | page.js
export const metadata = {
  other: {
    custom: ['meta1', 'meta2'],
  },
}
<head> output
<meta name="custom" content="meta1" /> <meta name="custom" content="meta2" />

不支援的中繼資料

以下中繼資料類型目前沒有內建支援。不過,仍然可以在佈局或頁面本身中渲染它們。

類型

你可以使用 Metadata 類型為你的元資料添加類型安全。如果你在 IDE 中使用內建的 TypeScript 插件,則無需手動添加類型,但你仍然可以明確添加它。

metadata 物件

layout.tsx | page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js',
}

generateMetadata 函式

一般函式
layout.tsx | page.tsx
import type { Metadata } from 'next'

export function generateMetadata(): Metadata {
  return {
    title: 'Next.js',
  }
}
非同步函式
layout.tsx | page.tsx
import type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}
帶有區段屬性
layout.tsx | page.tsx
import type { Metadata } from 'next'

type Props = {
  params: Promise<{ id: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}

export function generateMetadata({ params, searchParams }: Props): Metadata {
  return {
    title: 'Next.js',
  }
}

export default function Page({ params, searchParams }: Props) {}
帶有父元資料
layout.tsx | page.tsx
import type { Metadata, ResolvingMetadata } from 'next'

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}
JavaScript 專案

對於 JavaScript 專案,你可以使用 JSDoc 來添加類型安全。

layout.js | page.js
/** @type {import("next").Metadata} */
export const metadata = {
  title: 'Next.js',
}
元資料建議
<meta http-equiv="...">使用適當的 HTTP 標頭,透過 redirect()中介軟體 (Middleware)安全標頭 (Security Headers)
<base>在佈局 (layout) 或頁面 (page) 本身渲染該標籤。
<noscript>在佈局或頁面本身渲染該標籤。
<style>了解更多關於 Next.js 中的樣式設定
<script>了解更多關於 使用腳本
<link rel="stylesheet" />直接在佈局或頁面本身 import 樣式表。
<link rel="preload />使用 ReactDOM preload 方法
<link rel="preconnect" />使用 ReactDOM preconnect 方法
<link rel="dns-prefetch" />使用 ReactDOM prefetchDNS 方法

資源提示

<link> 元素有許多 rel 關鍵字,可用於向瀏覽器提示可能需要外部資源。瀏覽器根據這些關鍵字應用預載優化。

雖然元資料 API 不直接支援這些提示,但你可以使用新的 ReactDOM 方法 安全地將它們插入到文件的 <head> 中。

'use client'

import ReactDOM from 'react-dom'

export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')

  return '...'
}

在頁面渲染 (瀏覽器) 生命週期的早期開始載入資源。MDN 文件

ReactDOM.preload(href: string, options: { as: string })
<head> output
<link rel="preload" href="..." as="..." />

預先初始化與來源的連接。MDN 文件

ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })
<head> output
<link rel="preconnect" href="..." crossorigin />

在資源被請求之前嘗試解析域名。MDN 文件

ReactDOM.prefetchDNS(href: string)
<head> output
<link rel="dns-prefetch" href="..." />

須知:

  • 這些方法目前僅在客戶端元件 (Client Components) 中支援,這些元件在初始頁面載入時仍會進行伺服器端渲染 (SSR)。
  • Next.js 內建功能如 next/fontnext/imagenext/script 會自動處理相關的資源提示。

行為

預設欄位

有兩個預設的 meta 標籤總是會被添加,即使路由沒有定義元資料:

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

須知:你可以覆蓋預設的 viewport meta 標籤。

串流元資料

generateMetadata 返回的元資料會被串流到客戶端。這使得 Next.js 能夠在元資料解析後立即將其注入 HTML。

由於頁面元資料主要針對機器人和爬蟲,Next.js 會為能夠執行 JavaScript 並檢查完整頁面 DOM 的機器人(如 Googlebot)串流元資料。然而,對於 HTML 受限 的機器人(如 Twitterbot),元資料會繼續阻擋頁面渲染,因為這些機器人在爬取時無法執行 JavaScript。

Next.js 會自動檢測傳入請求的使用者代理,以決定是提供串流元資料還是回退到阻擋元資料。

如果需要自訂此列表,你可以使用 next.config.js 中的 htmlLimitedBots 選項手動定義它們。Next.js 會確保符合此正則表達式的使用者代理在請求你的網頁時收到阻擋元資料。

import type { NextConfig } from 'next'

const config: NextConfig = {
  htmlLimitedBots: /MySpecialBot|MyAnotherSpecialBot|SimpleCrawler/,
}

export default config

指定 htmlLimitedBots 配置將覆蓋 Next.js 的預設列表,讓你完全控制哪些使用者代理應選擇此行為。這是進階行為,預設值在大多數情況下已足夠。

順序

元資料按順序評估,從根區段開始,一直到最接近最終 page.js 區段的區段。例如:

  1. app/layout.tsx (根佈局)
  2. app/blog/layout.tsx (嵌套的部落格佈局)
  3. app/blog/[slug]/page.tsx (部落格頁面)

合併

遵循評估順序,從同一路由的多個區段導出的元資料物件會 淺層合併 在一起,形成路由的最終元資料輸出。重複的鍵會根據它們的順序 替換

這意味著嵌套欄位(如 openGraphrobots)在早期區段中定義的元資料會被最後一個定義它們的區段 覆蓋

覆蓋欄位

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// 輸出:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

在上面的例子中:

  • app/layout.js 中的 titleapp/blog/page.js 中的 title 替換
  • app/layout.js 中的所有 openGraph 欄位在 app/blog/page.js 中被 替換,因為 app/blog/page.js 設定了 openGraph 元資料。注意缺少 openGraph.description

如果你想在區段之間共享一些嵌套欄位,同時覆蓋其他欄位,可以將它們提取到單獨的變數中:

app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] }
app/page.js
import { openGraphImage } from './shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
}
app/about/page.js
import { openGraphImage } from '../shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}

在上面的例子中,OG 圖片在 app/layout.jsapp/about/page.js 之間共享,而標題則不同。

繼承欄位

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/about/page.js
export const metadata = {
  title: 'About',
}

// 輸出:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

注意

  • app/layout.js 中的 titleapp/about/page.js 中的 title 替換
  • app/layout.js 中的所有 openGraph 欄位在 app/about/page.js 中被 繼承,因為 app/about/page.js 沒有設定 openGraph 元資料。

版本歷史

版本變更
v15.2.0引入對 generateMetadata 的串流支援。
v13.2.0viewportthemeColorcolorScheme 被棄用,轉而支援 viewport 配置
v13.2.0引入 metadatagenerateMetadata